示例#1
0
def encode_output(value, output_file):
    """
    Encodes given value so it can be written to given file object.

    Value may be Unicode, binary string or any other data type.

    The exact behaviour depends on the Python version:

    Python 3.x

        `sys.stdout` is a `_io.TextIOWrapper` instance that accepts `str`
        (unicode) and breaks on `bytes`.

        It is OK to simply assume that everything is Unicode unless special
        handling is introduced in the client code.

        Thus, no additional processing is performed.

    Python 2.x

        `sys.stdout` is a file-like object that accepts `str` (bytes)
        and breaks when `unicode` is passed to `sys.stdout.write()`.

        We can expect both Unicode and bytes. They need to be encoded so as
        to match the file object encoding.

        The output is binary if the object doesn't explicitly require Unicode.

    """
    if sys.version_info > (3,0):
        # Python 3:  whatever → unicode
        return compat.text_type(value)
    else:
        # Python 2:  handle special cases
        stream_encoding = getattr(output_file, 'encoding', None)
        if stream_encoding:
            if stream_encoding.upper() == 'UTF-8':
                return compat.text_type(value)
            else:
                return value.encode(stream_encoding, 'ignore')
        else:
            # no explicit encoding requirements; force binary
            if isinstance(value, compat.text_type):
                # unicode → binary
                return value.encode('utf-8')
            else:
                return str(value)
示例#2
0
文件: io.py 项目: ach5910/KivyApp
def encode_output(value, output_file):
    """
    Encodes given value so it can be written to given file object.

    Value may be Unicode, binary string or any other data type.

    The exact behaviour depends on the Python version:

    Python 3.x

        `sys.stdout` is a `_io.TextIOWrapper` instance that accepts `str`
        (unicode) and breaks on `bytes`.

        It is OK to simply assume that everything is Unicode unless special
        handling is introduced in the client code.

        Thus, no additional processing is performed.

    Python 2.x

        `sys.stdout` is a file-like object that accepts `str` (bytes)
        and breaks when `unicode` is passed to `sys.stdout.write()`.

        We can expect both Unicode and bytes. They need to be encoded so as
        to match the file object encoding.

        The output is binary if the object doesn't explicitly require Unicode.

    """
    if sys.version_info > (3, 0):
        # Python 3:  whatever → unicode
        return compat.text_type(value)
    else:
        # Python 2:  handle special cases
        stream_encoding = getattr(output_file, 'encoding', None)
        if stream_encoding:
            if stream_encoding.upper() == 'UTF-8':
                return compat.text_type(value)
            else:
                return value.encode(stream_encoding, 'ignore')
        else:
            # no explicit encoding requirements; force binary
            if isinstance(value, compat.text_type):
                # unicode → binary
                return value.encode('utf-8')
            else:
                return str(value)
示例#3
0
def confirm(action, default=None, skip=False):
    """A shortcut for typical confirmation prompt.

    :param action:

        a string describing the action, e.g. "Apply changes". A question mark
        will be appended.

    :param default:

        `bool` or `None`. Determines what happens when user hits :kbd:`Enter`
        without typing in a choice. If `True`, default choice is "yes". If
        `False`, it is "no". If `None` the prompt keeps reappearing until user
        types in a choice (not necessarily acceptable) or until the number of
        iteration reaches the limit. Default is `None`.

    :param skip:

        `bool`; if `True`, no interactive prompt is used and default choice is
        returned (useful for batch mode). Default is `False`.

    Usage::

        def delete(key, silent=False):
            item = db.get(Item, args.key)
            if confirm('Delete '+item.title, default=True, skip=silent):
                item.delete()
                print('Item deleted.')
            else:
                print('Operation cancelled.')

    Returns `None` on `KeyboardInterrupt` event.
    """
    MAX_ITERATIONS = 3
    if skip:
        return default
    else:
        defaults = {None: ("y", "n"), True: ("Y", "n"), False: ("y", "N")}
        y, n = defaults[default]
        prompt = text_type("{action}? ({y}/{n})").format(**locals())
        choice = None
        try:
            if default is None:
                cnt = 1
                while not choice and cnt < MAX_ITERATIONS:
                    choice = safe_input(prompt)
                    cnt += 1
            else:
                choice = safe_input(prompt)
        except KeyboardInterrupt:
            return None
    if choice in ("yes", "y", "Y"):
        return True
    if choice in ("no", "n", "N"):
        return False
    if default is not None:
        return default
    return None
示例#4
0
def _execute_command(args, errors_file):
    """
    Asserts that ``args.function`` is present and callable. Tries different
    approaches to calling the function (with an `argparse.Namespace` object or
    with ordinary signature). Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    assert hasattr(args, 'function') and hasattr(args.function, '__call__')

    # the function is nested to catch certain exceptions (see below)
    def _call():
        # Actually call the function
        if getattr(args.function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result = args.function(args)
        else:
            # namespace -> dictionary
            _flat_key = lambda key: key.replace('-', '_')
            all_input = dict((_flat_key(k), v) for k,v in  vars(args).items())

            # filter the namespace variables so that only those expected by the
            # actual function will pass

            spec = get_arg_spec(args.function)

            positional = [all_input[k] for k in spec.args]
            kwonly = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kwonly)

            # *args
            if spec.varargs:
                positional += getattr(args, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = ['function'] + spec.args + [spec.varargs] + kwonly
                extra = [k for k in vars(args) if k not in not_kwargs]
                for k in extra:
                    keywords[k] = getattr(args, k)

            result = args.function(*positional, **keywords)

        # Yield the results
        if isinstance(result, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line in result:
                yield line
        else:
            # yield non-empty non-iterable result as a single line
            if result is not None:
                yield result

    wrappable_exceptions = [CommandError]
    wrappable_exceptions += getattr(args.function, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        result = _call()
        for line in result:
            yield line
    except tuple(wrappable_exceptions) as e:
        processor = getattr(args.function, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
                            lambda e: '{0.__class__.__name__}: {0}'.format(e))

        errors_file.write(compat.text_type(processor(e)))
        errors_file.write('\n')
def _execute_command(function, namespace_obj, errors_file, pre_call=None):
    """
    Assumes that `function` is a callable.  Tries different approaches
    to call it (with `namespace_obj` or with ordinary signature).
    Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    if pre_call:  # XXX undocumented because I'm unsure if it's OK
        # Actually used in real projects:
        # * https://google.com/search?q=argh+dispatch+pre_call
        # * https://github.com/neithere/argh/issues/63
        pre_call(namespace_obj)

    # the function is nested to catch certain exceptions (see below)
    def _call():
        # Actually call the function
        if getattr(function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result = function(namespace_obj)
        else:
            # namespace -> dictionary
            _flat_key = lambda key: key.replace('-', '_')
            all_input = dict((_flat_key(k), v)
                             for k,v in vars(namespace_obj).items())

            # filter the namespace variables so that only those expected
            # by the actual function will pass

            spec = get_arg_spec(function)

            positional = [all_input[k] for k in spec.args]
            kwonly = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kwonly)

            # *args
            if spec.varargs:
                positional += getattr(namespace_obj, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = [DEST_FUNCTION] + spec.args + [spec.varargs] + kwonly
                for k in vars(namespace_obj):
                    if k.startswith('_') or k in not_kwargs:
                        continue
                    keywords[k] = getattr(namespace_obj, k)

            result = function(*positional, **keywords)

        # Yield the results
        if isinstance(result, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line in result:
                yield line
        else:
            # yield non-empty non-iterable result as a single line
            if result is not None:
                yield result

    wrappable_exceptions = [CommandError]
    wrappable_exceptions += getattr(function, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        result = _call()
        for line in result:
            yield line
    except tuple(wrappable_exceptions) as e:
        processor = getattr(function, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
                            lambda e: '{0.__class__.__name__}: {0}'.format(e))

        errors_file.write(compat.text_type(processor(e)))
        errors_file.write('\n')
示例#6
0
def confirm(action, default=None, skip=False):
    """A shortcut for typical confirmation prompt.

    :param action:

        a string describing the action, e.g. "Apply changes". A question mark
        will be appended.

    :param default:

        `bool` or `None`. Determines what happens when user hits :kbd:`Enter`
        without typing in a choice. If `True`, default choice is "yes". If
        `False`, it is "no". If `None` the prompt keeps reappearing until user
        types in a choice (not necessarily acceptable) or until the number of
        iteration reaches the limit. Default is `None`.

    :param skip:

        `bool`; if `True`, no interactive prompt is used and default choice is
        returned (useful for batch mode). Default is `False`.

    Usage::

        def delete(key, silent=False):
            item = db.get(Item, args.key)
            if confirm('Delete '+item.title, default=True, skip=silent):
                item.delete()
                print('Item deleted.')
            else:
                print('Operation cancelled.')

    Returns `None` on `KeyboardInterrupt` event.
    """
    MAX_ITERATIONS = 3
    if skip:
        return default
    else:
        defaults = {
            None: ('y', 'n'),
            True: ('Y', 'n'),
            False: ('y', 'N'),
        }
        y, n = defaults[default]
        prompt = text_type('{action}? ({y}/{n})').format(**locals())
        choice = None
        try:
            if default is None:
                cnt = 1
                while not choice and cnt < MAX_ITERATIONS:
                    choice = safe_input(prompt)
                    cnt += 1
            else:
                choice = safe_input(prompt)
        except KeyboardInterrupt:
            return None
    if choice in ('yes', 'y', 'Y'):
        return True
    if choice in ('no', 'n', 'N'):
        return False
    if default is not None:
        return default
    return None
示例#7
0
def _execute_command(function, namespace_obj, errors_file, pre_call=None):
    """
    Assumes that `function` is a callable.  Tries different approaches
    to call it (with `namespace_obj` or with ordinary signature).
    Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    if pre_call:  # XXX undocumented because I'm unsure if it's OK
        # Actually used in real projects:
        # * https://google.com/search?q=argh+dispatch+pre_call
        # * https://github.com/neithere/argh/issues/63
        pre_call(namespace_obj)

    # the function is nested to catch certain exceptions (see below)
    def _call():
        # Actually call the function
        if getattr(function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result = function(namespace_obj)
        else:
            # namespace -> dictionary
            _flat_key = lambda key: key.replace('-', '_')
            all_input = dict(
                (_flat_key(k), v) for k, v in vars(namespace_obj).items())

            # filter the namespace variables so that only those expected
            # by the actual function will pass

            spec = get_arg_spec(function)

            positional = [all_input[k] for k in spec.args]
            kwonly = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kwonly)

            # *args
            if spec.varargs:
                positional += getattr(namespace_obj, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = [DEST_FUNCTION] + spec.args + [spec.varargs
                                                            ] + kwonly
                for k in vars(namespace_obj):
                    if k.startswith('_') or k in not_kwargs:
                        continue
                    keywords[k] = getattr(namespace_obj, k)

            result = function(*positional, **keywords)

        # Yield the results
        if isinstance(result, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line in result:
                yield line
        else:
            # yield non-empty non-iterable result as a single line
            if result is not None:
                yield result

    wrappable_exceptions = [CommandError]
    wrappable_exceptions += getattr(function, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        result = _call()
        for line in result:
            yield line
    except tuple(wrappable_exceptions) as e:
        processor = getattr(function, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
                            lambda e: '{0.__class__.__name__}: {0}'.format(e))

        errors_file.write(compat.text_type(processor(e)))
        errors_file.write('\n')
示例#8
0
def _execute_command(args, errors_file):
    """
    Asserts that ``args.function`` is present and callable. Tries different
    approaches to calling the function (with an `argparse.Namespace` object or
    with ordinary signature). Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    assert hasattr(args, 'function') and hasattr(args.function, '__call__')

    # the function is nested to catch certain exceptions (see below)
    def _call():
        # Actually call the function
        if getattr(args.function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result = args.function(args)
        else:
            # namespace -> dictionary
            _flat_key = lambda key: key.replace('-', '_')
            all_input = dict((_flat_key(k), v) for k,v in  vars(args).items())

            # filter the namespace variables so that only those expected by the
            # actual function will pass

            spec = get_arg_spec(args.function)

            positional = [all_input[k] for k in spec.args]
            kwonly = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kwonly)

            # *args
            if spec.varargs:
                positional += getattr(args, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = ['function'] + spec.args + [spec.varargs] + kwonly
                extra = [k for k in vars(args) if k not in not_kwargs]
                for k in extra:
                    keywords[k] = getattr(args, k)

            result = args.function(*positional, **keywords)

        # Yield the results
        if isinstance(result, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line in result:
                yield line
        else:
            # yield non-empty non-iterable result as a single line
            if result is not None:
                yield result

    wrappable_exceptions = [CommandError]
    wrappable_exceptions += getattr(args.function, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        result = _call()
        for line in result:
            yield line
    except tuple(wrappable_exceptions) as e:
        processor = getattr(args.function, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
                            lambda e: '{0.__class__.__name__}: {0}'.format(e))

        errors_file.write(compat.text_type(processor(e)))
        errors_file.write('\n')
示例#9
0
def _execute_command(func, namespace_obj, pre_call=None):  # noqa: C901
    # noinspection SpellCheckingInspection
    """
    Assumes that `function` is a callable.  Tries different approaches
    to call it (with `namespace_obj` or with ordinary signature).
    Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    if pre_call:
        LOGGER.debug('running pre_call: %s', pre_call)
        pre_call(namespace_obj)

    # namespace -> dictionary
    def _flat_key(key):
        return key.replace('-', '_')

    # noinspection SpellCheckingInspection
    def _call():
        # Actually call the function
        if getattr(func, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result_ = func(namespace_obj)
        else:

            all_input = dict((_flat_key(k), v)
                             for k, v in vars(namespace_obj).items())

            # filter the namespace variables so that only those expected
            # by the actual function will pass

            spec = get_arg_spec(func)

            positional = [all_input[k] for k in spec.args]
            # noinspection SpellCheckingInspection
            kw_only = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kw_only)

            # *args
            if spec.varargs:
                positional += getattr(namespace_obj, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = [DEST_FUNCTION] + spec.args + [spec.varargs] + kw_only
                for k in vars(namespace_obj):
                    if k.startswith('_') or k in not_kwargs:
                        continue
                    keywords[k] = getattr(namespace_obj, k)

            result_ = func(*positional, **keywords)

        # Yield the results
        if isinstance(result_, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line_ in result_:
                yield line_
        else:
            # yield non-empty non-iterable result as a single line
            if result_ is not None:
                yield result_

    # noinspection SpellCheckingInspection
    wrappable_exceptions = [CommandError, Exception]
    wrappable_exceptions += getattr(func, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        LOGGER.debug('running func: %s', func)
        result = _call()
        return '\n'.join(result)
    # pylint: disable=catching-non-exception
    except tuple(wrappable_exceptions) as exc:
        # pylint: disable=unnecessary-lambda
        processor = getattr(
            func, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
            lambda exc_: '{0.__class__.__name__}: {0}'.format(exc_)
        )

        LOGGER.error(compat.text_type(processor(exc)))
        LOGGER.exception(exc)