Commits

Andy Mikhailenko committed dd4dd4c

Fix issue #32: replace bundled "six" library with a small chunk of code

  • Participants
  • Parent commits 4f8b689

Comments (0)

Files changed (10)

argh/assembling.py

 
 Functions and classes to properly assemble your commands in a parser.
 """
+import sys
 import argparse
-import inspect
 
-from argh.six import PY3, string_types
 from argh.constants import (ATTR_ALIASES, ATTR_ARGS, ATTR_NAME,
                             ATTR_INFER_ARGS_FROM_SIGNATURE,
                             ATTR_EXPECTS_NAMESPACE_OBJECT)
 from argh.utils import get_subparsers
+from argh import compat
 
 
 __all__ = ['SUPPORTS_ALIASES', 'set_default_command', 'add_commands']
     if getattr(function, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
         return
 
-    if PY3:
-        spec = inspect.getfullargspec(function)
-    else:
-        spec = inspect.getargspec(function)
+    spec = compat.getargspec(function)
 
     kwargs = dict(zip(*[reversed(x) for x in (spec.args, spec.defaults or [])]))
 
-    if PY3:
+    if sys.version_info < (3,0):
+        annotations = {}
+    else:
         annotations = dict((k,v) for k,v in function.__annotations__.items()
                            if isinstance(v, str))
-    else:
-        annotations = {}
 
     # define the list of conflicting option strings
     # (short forms, i.e. single-character ones)
     # It's very likely that in the latter case the function accepts one and
     # only argument named "args".  If so, we simply wrap this function in
     # @expects_obj and issue a warning.
-    if PY3:
-        spec = inspect.getfullargspec(function)
-    else:
-        spec = inspect.getargspec(function)
+    spec = compat.getargspec(function)
+
     if spec.args == ['args']:
         # this is it -- a classic old-style function, goddamnit.
         # no checking *args and **kwargs because they are unlikely to matter.
 
     function = _fix_compat_issue29(function)
 
-    if PY3:
-        spec = inspect.getfullargspec(function)
-    else:
-        spec = inspect.getargspec(function)
+    spec = compat.getargspec(function)
 
     declared_args = getattr(function, ATTR_ARGS, [])
     inferred_args = list(_get_args_from_signature(function))
 
     if namespace:
         # make a namespace placeholder and register the commands within it
-        assert isinstance(namespace, string_types)
         subsubparser = subparsers.add_parser(namespace, help=title)
         subparsers = subsubparser.add_subparsers(title=title,
                                                  description=description,
+# based on "six" by Benjamin Peterson
+
+import inspect
+import sys
+
+
+if sys.version_info < (3,0):
+    text_type = unicode
+    binary_type = str
+
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+else:
+    text_type = str
+    binary_type = bytes
+
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+
+
+if sys.version_info < (3,0):
+    getargspec = inspect.getargspec
+else:
+    # in Python 3 the basic getargspec doesn't support keyword-only arguments
+    # and annotations and raises ValueError if they are discovered
+    getargspec = inspect.getfullargspec
+

argh/dispatching.py

 import sys
 from types import GeneratorType
 
-from argh.six import text_type, BytesIO, StringIO, PY3
-
+from argh import compat, io
 from argh.constants import (ATTR_WRAPPED_EXCEPTIONS,
                             ATTR_EXPECTS_NAMESPACE_OBJECT)
 from argh.completion import autocomplete
 from argh.assembling import add_commands, set_default_command
 from argh.exceptions import CommandError
-from argh import io
 
 
 __all__ = ['dispatch', 'dispatch_command', 'dispatch_commands']
     if output_file is None:
         # user wants a string; we create an internal temporary file-like object
         # and will return its contents as a string
-        f = StringIO() if PY3 else BytesIO()
+        if sys.version_info < (3,0):
+            f = compat.BytesIO()
+        else:
+            f = compat.StringIO()
     else:
         # normally this is stdout; can be any file
         f = output_file
             # filter the namespace variables so that only those expected by the
             # actual function will pass
 
-            f = args.function
-            if PY3:
-                spec = inspect.getfullargspec(f)
-            else:
-                spec = inspect.getargspec(f)
+            spec = compat.getargspec(args.function)
 
             positional = [all_input[k] for k in spec.args]
             keywords = {}
         for line in result:
             yield line
     except tuple(wrappable_exceptions) as e:
-        yield text_type(e)
+        yield compat.text_type(e)
 
 
 def dispatch_command(function, *args, **kwargs):

argh/interaction.py

 Interaction
 ~~~~~~~~~~~
 """
-from argh.six import text_type
-from argh.io import safe_input
+from argh import compat, io
 
 
 __all__ = ['confirm']
             False: ('y','N'),
         }
         y, n = defaults[default]
-        prompt = text_type('{action}? ({y}/{n})').format(**locals())
+        prompt = compat.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)
+                    choice = io.safe_input(prompt)
                     cnt += 1
             else:
-                choice = safe_input(prompt)
+                choice = io.safe_input(prompt)
         except KeyboardInterrupt:
             return None
     if choice in ('yes', 'y', 'Y'):
 ~~~~~~~~~~~~~~~~~
 """
 import locale
+import sys
 
-from argh.six import binary_type, text_type, PY3
+from argh import compat
 
 
 __all__ = ['dump', 'encode_output', 'safe_input']
 
 def _input(prompt):
     # this function can be mocked up in tests
-    if PY3:
+    if sys.version_info < (3,0):
+        return raw_input(prompt)
+    else:
         return input(prompt)
-    else:
-        return raw_input(prompt)
 
 
 def safe_input(prompt):
     "Prompts user for input. Correctly handles prompt message encoding."
 
-    if PY3:
-        if not isinstance(prompt, text_type):
-            # Python 3.x: bytes →  unicode
-            prompt = prompt.decode()
-    else:
-        if isinstance(prompt, text_type):
+    if sys.version_info < (3,0):
+        if isinstance(prompt, compat.text_type):
             # Python 2.x: unicode →  bytes
             encoding = locale.getpreferredencoding() or 'utf-8'
             prompt = prompt.encode(encoding)
+    else:
+        if not isinstance(prompt, compat.text_type):
+            # Python 3.x: bytes →  unicode
+            prompt = prompt.decode()
 
     return _input(prompt)
 
         The output is binary if the object is a TTY; otherwise it's Unicode.
 
     """
-    if not PY3:
+    if sys.version_info < (3,0):
         # handle special cases in Python 2.x
 
         if output_file.isatty():
             # TTY expects bytes
-            if isinstance(value, text_type):
+            if isinstance(value, compat.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)
+            return compat.binary_type(value)
 
-        if isinstance(value, binary_type):
+        if isinstance(value, compat.binary_type):
             # binary →  unicode
             return value.decode('utf-8')
 
     # whatever → unicode
-    return text_type(value)
+    return compat.text_type(value)
 
 
 def dump(raw_data, output_file):

argh/six.py

-#
-# Copyright (c) 2010-2011 Benjamin Peterson
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy of
-# this software and associated documentation files (the "Software"), to deal in
-# the Software without restriction, including without limitation the rights to
-# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-# the Software, and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS
-# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-"""Utilities for writing code that runs on Python 2 and 3"""
-
-import operator
-import sys
-import types
-
-__author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.1.0"
-
-
-# True if we are running on Python 3.
-PY3 = sys.version_info[0] == 3
-
-if PY3:
-    string_types = str,
-    integer_types = int,
-    class_types = type,
-    text_type = str
-    binary_type = bytes
-
-    MAXSIZE = sys.maxsize
-else:
-    string_types = basestring,
-    integer_types = (int, long)
-    class_types = (type, types.ClassType)
-    text_type = unicode
-    binary_type = str
-
-    # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
-    class X(object):
-        def __len__(self):
-            return 1 << 31
-    try:
-        len(X())
-    except OverflowError:
-        # 32-bit
-        MAXSIZE = int((1 << 31) - 1)
-    else:
-        # 64-bit
-        MAXSIZE = int((1 << 63) - 1)
-    del X
-
-
-def _add_doc(func, doc):
-    """Add documentation to a function."""
-    func.__doc__ = doc
-
-
-def _import_module(name):
-    """Import module, returning the module after the last dot."""
-    __import__(name)
-    return sys.modules[name]
-
-
-class _LazyDescr(object):
-
-    def __init__(self, name):
-        self.name = name
-
-    def __get__(self, obj, tp):
-        result = self._resolve()
-        setattr(obj, self.name, result)
-        # This is a bit ugly, but it avoids running this again.
-        delattr(tp, self.name)
-        return result
-
-
-class MovedModule(_LazyDescr):
-
-    def __init__(self, name, old, new=None):
-        super(MovedModule, self).__init__(name)
-        if PY3:
-            if new is None:
-                new = name
-            self.mod = new
-        else:
-            self.mod = old
-
-    def _resolve(self):
-        return _import_module(self.mod)
-
-
-class MovedAttribute(_LazyDescr):
-
-    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
-        super(MovedAttribute, self).__init__(name)
-        if PY3:
-            if new_mod is None:
-                new_mod = name
-            self.mod = new_mod
-            if new_attr is None:
-                if old_attr is None:
-                    new_attr = name
-                else:
-                    new_attr = old_attr
-            self.attr = new_attr
-        else:
-            self.mod = old_mod
-            if old_attr is None:
-                old_attr = name
-            self.attr = old_attr
-
-    def _resolve(self):
-        module = _import_module(self.mod)
-        return getattr(module, self.attr)
-
-
-
-class _MovedItems(types.ModuleType):
-    """Lazy loading of moved objects"""
-
-
-_moved_attributes = [
-    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
-    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
-    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
-    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
-    MovedAttribute("reduce", "__builtin__", "functools"),
-    MovedAttribute("StringIO", "StringIO", "io"),
-    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
-    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
-
-    MovedModule("builtins", "__builtin__"),
-    MovedModule("configparser", "ConfigParser"),
-    MovedModule("copyreg", "copy_reg"),
-    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
-    MovedModule("http_cookies", "Cookie", "http.cookies"),
-    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
-    MovedModule("html_parser", "HTMLParser", "html.parser"),
-    MovedModule("http_client", "httplib", "http.client"),
-    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
-    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
-    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
-    MovedModule("cPickle", "cPickle", "pickle"),
-    MovedModule("queue", "Queue"),
-    MovedModule("reprlib", "repr"),
-    MovedModule("socketserver", "SocketServer"),
-    MovedModule("tkinter", "Tkinter"),
-    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
-    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
-    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
-    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
-    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
-    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
-    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
-    MovedModule("tkinter_colorchooser", "tkColorChooser",
-                "tkinter.colorchooser"),
-    MovedModule("tkinter_commondialog", "tkCommonDialog",
-                "tkinter.commondialog"),
-    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
-    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
-    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
-    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
-                "tkinter.simpledialog"),
-    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
-    MovedModule("winreg", "_winreg"),
-]
-for attr in _moved_attributes:
-    setattr(_MovedItems, attr.name, attr)
-del attr
-
-moves = sys.modules["six.moves"] = _MovedItems("moves")
-
-
-def add_move(move):
-    """Add an item to six.moves."""
-    setattr(_MovedItems, move.name, move)
-
-
-def remove_move(name):
-    """Remove item from six.moves."""
-    try:
-        delattr(_MovedItems, name)
-    except AttributeError:
-        try:
-            del moves.__dict__[name]
-        except KeyError:
-            raise AttributeError("no such move, %r" % (name,))
-
-
-if PY3:
-    _meth_func = "__func__"
-    _meth_self = "__self__"
-
-    _func_code = "__code__"
-    _func_defaults = "__defaults__"
-
-    _iterkeys = "keys"
-    _itervalues = "values"
-    _iteritems = "items"
-else:
-    _meth_func = "im_func"
-    _meth_self = "im_self"
-
-    _func_code = "func_code"
-    _func_defaults = "func_defaults"
-
-    _iterkeys = "iterkeys"
-    _itervalues = "itervalues"
-    _iteritems = "iteritems"
-
-
-try:
-    advance_iterator = next
-except NameError:
-    def advance_iterator(it):
-        return it.next()
-next = advance_iterator
-
-
-if PY3:
-    def get_unbound_function(unbound):
-        return unbound
-
-    Iterator = object
-
-    def callable(obj):
-        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-else:
-    def get_unbound_function(unbound):
-        return unbound.im_func
-
-    class Iterator(object):
-
-        def next(self):
-            return type(self).__next__(self)
-
-    callable = callable
-_add_doc(get_unbound_function,
-         """Get the function out of a possibly unbound function""")
-
-
-get_method_function = operator.attrgetter(_meth_func)
-get_method_self = operator.attrgetter(_meth_self)
-get_function_code = operator.attrgetter(_func_code)
-get_function_defaults = operator.attrgetter(_func_defaults)
-
-
-def iterkeys(d):
-    """Return an iterator over the keys of a dictionary."""
-    return getattr(d, _iterkeys)()
-
-def itervalues(d):
-    """Return an iterator over the values of a dictionary."""
-    return getattr(d, _itervalues)()
-
-def iteritems(d):
-    """Return an iterator over the (key, value) pairs of a dictionary."""
-    return getattr(d, _iteritems)()
-
-
-if PY3:
-    def b(s):
-        return s.encode("latin-1")
-    def u(s):
-        return s
-    if sys.version_info[1] <= 1:
-        def int2byte(i):
-            return bytes((i,))
-    else:
-        # This is about 2x faster than the implementation above on 3.2+
-        int2byte = operator.methodcaller("to_bytes", 1, "big")
-    import io
-    StringIO = io.StringIO
-    BytesIO = io.BytesIO
-else:
-    def b(s):
-        return s
-    def u(s):
-        return unicode(s, "unicode_escape")
-    int2byte = chr
-    import StringIO
-    StringIO = BytesIO = StringIO.StringIO
-_add_doc(b, """Byte literal""")
-_add_doc(u, """Text literal""")
-
-
-if PY3:
-    import builtins
-    exec_ = getattr(builtins, "exec")
-
-
-    def reraise(tp, value, tb=None):
-        if value.__traceback__ is not tb:
-            raise value.with_traceback(tb)
-        raise value
-
-
-    print_ = getattr(builtins, "print")
-    del builtins
-
-else:
-    def exec_(code, globs=None, locs=None):
-        """Execute code in a namespace."""
-        if globs is None:
-            frame = sys._getframe(1)
-            globs = frame.f_globals
-            if locs is None:
-                locs = frame.f_locals
-            del frame
-        elif locs is None:
-            locs = globs
-        exec("""exec code in globs, locs""")
-
-
-    exec_("""def reraise(tp, value, tb=None):
-    raise tp, value, tb
-""")
-
-
-    def print_(*args, **kwargs):
-        """The new-style print function."""
-        fp = kwargs.pop("file", sys.stdout)
-        if fp is None:
-            return
-        def write(data):
-            if not isinstance(data, basestring):
-                data = str(data)
-            fp.write(data)
-        want_unicode = False
-        sep = kwargs.pop("sep", None)
-        if sep is not None:
-            if isinstance(sep, unicode):
-                want_unicode = True
-            elif not isinstance(sep, str):
-                raise TypeError("sep must be None or a string")
-        end = kwargs.pop("end", None)
-        if end is not None:
-            if isinstance(end, unicode):
-                want_unicode = True
-            elif not isinstance(end, str):
-                raise TypeError("end must be None or a string")
-        if kwargs:
-            raise TypeError("invalid keyword arguments to print()")
-        if not want_unicode:
-            for arg in args:
-                if isinstance(arg, unicode):
-                    want_unicode = True
-                    break
-        if want_unicode:
-            newline = unicode("\n")
-            space = unicode(" ")
-        else:
-            newline = "\n"
-            space = " "
-        if sep is None:
-            sep = space
-        if end is None:
-            end = newline
-        for i, arg in enumerate(args):
-            if i:
-                write(sep)
-            write(arg)
-        write(end)
-
-_add_doc(reraise, """Reraise an exception.""")
-
-
-def with_metaclass(meta, base=object):
-    """Create a base class with a metaclass."""
-    return meta("NewBase", (base,), {})
 Common stuff for tests
 ~~~~~~~~~~~~~~~~~~~~~~
 """
+import sys
+
 from argh import ArghParser
-from argh.six import  PY3, BytesIO, StringIO, string_types
+from argh.compat import BytesIO, StringIO
 
 
 class DebugArghParser(ArghParser):
 
 def make_IO():
     "Returns a file object of the same type as `sys.stdout`."
-    if PY3:
+    if sys.version_info < (3,0):
+        return BytesIO()
+    else:
         return StringIO()
-    else:
-        return BytesIO()
 
 
 def call_cmd(parser, command_string, **kwargs):
-    if isinstance(command_string, string_types):
+    if hasattr(command_string, 'split'):
         args = command_string.split()
     else:
         args = command_string

test/test_assembling.py

 Unit Tests For Assembling Phase
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 """
+import sys
 import mock
 import pytest
 
 
 def test_annotation():
     "Extracting argument help from function annotations (Python 3 only)."
-    if not argh.six.PY3:
+    if sys.version_info < (3,0):
         pytest.skip('unsupported configuration')
 
     # Yes, this looks horrible, but otherwise Python 2 would die

test/test_dispatching.py

 
 
 def _dispatch_and_capture(func, command_string, **kwargs):
-    if isinstance(command_string, argh.six.string_types):
+    if hasattr(command_string, 'split'):
         args = command_string.split()
     else:
         args = command_string

test/test_interaction.py

 Interaction Tests
 ~~~~~~~~~~~~~~~~~
 """
+import sys
 import argh
 
 
 def test_encoding():
     "Unicode and bytes are accepted as prompt message"
     def raw_input_mock(prompt):
-        if not argh.six.PY3:
-            assert isinstance(prompt, argh.six.binary_type)
+        if sys.version_info <= (3,0):
+            assert isinstance(prompt, argh.compat.binary_type)
     argh.io._input = raw_input_mock
-    argh.confirm(argh.six.u('привет'))
+    msg = 'привет'
+    if sys.version_info <= (3,0):
+        msg = unicode(msg, 'unicode_escape')
+    argh.confirm(msg)