Andy Mikhailenko committed f3616dc

Fix issue #24: remove magic from output encoding. NOTE: dropped argument `encoding` in `dispatch()`.

  • Participants
  • Parent commits a1d2580

Comments (0)

Files changed (3)

File argh/

 import argparse
 import sys
 from argh.completion import autocomplete
 from argh.assembling import add_commands, set_default_command
 from argh.exceptions import CommandError
-from argh.output import encode_output
+from argh import output
 __all__ = ['dispatch', 'dispatch_command', 'dispatch_commands']
-def dispatch(parser, argv=None, add_help_command=True, encoding=None,
+def dispatch(parser, argv=None, add_help_command=True,
              completion=True, pre_call=None, output_file=sys.stdout,
              raw_output=False, namespace=None):
     """Parses given list of arguments using given parser, calls the relevant
         argument so that ``help foo`` becomes ``foo --help`` and displays usage
         information for "foo". Default is `True`.
-    :param encoding:
-        Encoding for results. If `None`, it is determined automatically.
-        Default is `None`.
     :param output_file:
         A file-like object for output. If `None`, the resulting lines are
         # displayed to the user before anything else happens, e.g.
         # raw_input() is called
-        output = encode_output(line, f, encoding) or ''
-        f.write(output)
+        output.dump(line, f)
         if not raw_output:
             # in most cases user wants on message per line
-            f.write('\n')
+            output.dump('\n', f)
     if output_file is None:
         # user wanted a string; return contents of our temporary file-like obj

File argh/

 import locale
 from argh.six import binary_type, text_type, PY3
-__all__ = ['encode_output']
+__all__ = ['dump', 'encode_output']
-def encode_output(line, output_file, encoding=None):
-    """Converts given string to given encoding. If no encoding is specified, it
-    is determined from terminal settings or, if none, from system settings.
+def encode_output(value, output_file):
+    """ Encodes given value so it can be written to given file object.
-    .. note:: Compatibility
+    Value may be Unicode, binary string or any other data type.
-       :Python 2.x:
-           `sys.stdout` is a file-like object that accepts `str` (bytes)
-           and breaks when `unicode` is passed to `sys.stdout.write()`.
-       :Python 3.x:
-           `sys.stdout` is a `_io.TextIOWrapper` instance that accepts `str`
-           (unicode) and breaks on `bytes`.
+    The exact behaviour depends on the Python version:
-       In Python 2.x arbitrary types are coerced to `unicode` and then to `str`.
+    Python 3.x
-       In Python 3.x all types are coerced to `str` with the exception
-       for `bytes` which is **not allowed** to avoid confusion.
+        `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 is a TTY; otherwise it's Unicode.
-    if not isinstance(line, text_type):
-        if PY3 and isinstance(line, binary_type):
-            # in Python 3.x we require Unicode, period.
-            raise TypeError('Binary comand output is not supported '
-                            'in Python 3.x')
+    if not PY3:
+        # handle special cases in Python 2.x
-        # in Python 2.x we accept bytes and convert them to Unicode.
-        try:
-            line = text_type(line)
-        except UnicodeDecodeError:
-            line = binary_type(line).decode('utf-8')
+        if output_file.isatty():
+            # TTY expects bytes
+            if isinstance(value, text_type):
+                encoding = getattr(output_file, 'encoding', None)
+                encoding = encoding or locale.getpreferredencoding() or 'utf-8'
+                # unicode →  binary
+                return value.encode(encoding)
+            return binary_type(value)
-    if PY3:
-        return line
+        if isinstance(value, binary_type):
+            # binary →  unicode
+            return value.decode('utf-8')
-    # Choose output encoding
-    if not encoding:
-        # choose between terminal's and system's preferred encodings
-        if output_file.isatty():
-            encoding = getattr(output_file, 'encoding', None)
+    # whatever → unicode
+    return text_type(value)
-        encoding = encoding or locale.getpreferredencoding()
-    # Convert string from Unicode to the output encoding
-    return line.encode(encoding)
+def dump(raw_data, output_file):
+    """ Writes given line to given output file.
+    See :func:`encode_output` for details.
+    """
+    data = encode_output(raw_data, output_file)
+    output_file.write(data)

File argh/

 #  General Public License version 3 (LGPLv3) as published by the Free
 #  Software Foundation. See the file README for copying conditions.
 import argparse