1# originally inspired by "six" by Benjamin Peterson
2
3import inspect
4import sys
5
6
7if sys.version_info < (3,0):
8    text_type = unicode
9    binary_type = str
10
11    import StringIO
12    StringIO = BytesIO = StringIO.StringIO
13else:
14    text_type = str
15    binary_type = bytes
16
17    import io
18    StringIO = io.StringIO
19    BytesIO = io.BytesIO
20
21
22def getargspec_permissive(func):
23    """
24    An `inspect.getargspec` with a relaxed sanity check to support Cython.
25
26    Motivation:
27
28        A Cython-compiled function is *not* an instance of Python's
29        types.FunctionType.  That is the sanity check the standard Py2
30        library uses in `inspect.getargspec()`.  So, an exception is raised
31        when calling `argh.dispatch_command(cythonCompiledFunc)`.  However,
32        the CyFunctions do have perfectly usable `.func_code` and
33        `.func_defaults` which is all `inspect.getargspec` needs.
34
35        This function just copies `inspect.getargspec()` from the standard
36        library but relaxes the test to a more duck-typing one of having
37        both `.func_code` and `.func_defaults` attributes.
38    """
39    if inspect.ismethod(func):
40        func = func.im_func
41
42    # Py2 Stdlib uses isfunction(func) which is too strict for Cython-compiled
43    # functions though such have perfectly usable func_code, func_defaults.
44    if not (hasattr(func, "func_code") and hasattr(func, "func_defaults")):
45        raise TypeError('{!r} missing func_code or func_defaults'.format(func))
46
47    args, varargs, varkw = inspect.getargs(func.func_code)
48    return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
49
50
51if sys.version_info < (3,0):
52    getargspec = getargspec_permissive
53else:
54    # in Python 3 the basic getargspec doesn't support keyword-only arguments
55    # and annotations and raises ValueError if they are discovered
56    getargspec = inspect.getfullargspec
57
58
59class _PrimitiveOrderedDict(dict):
60    """
61    A poor man's OrderedDict replacement for compatibility with Python 2.6.
62    Implements only the basic features.  May easily break if non-overloaded
63    methods are used.
64    """
65    def __init__(self, *args, **kwargs):
66        super(_PrimitiveOrderedDict, self).__init__(*args, **kwargs)
67        self._seq = []
68
69    def __setitem__(self, key, value):
70        super(_PrimitiveOrderedDict, self).__setitem__(key, value)
71        if key not in self._seq:
72            self._seq.append(key)
73
74    def __delitem__(self, key):
75        super(_PrimitiveOrderedDict, self).__delitem__(key)
76        idx = self._seq.index(key)
77        del self._seq[idx]
78
79    def __iter__(self):
80        return iter(self._seq)
81
82    def keys(self):
83        return list(self)
84
85    def values(self):
86        return [self[k] for k in self]
87
88
89try:
90    from collections import OrderedDict
91except ImportError:
92    OrderedDict = _PrimitiveOrderedDict
93