1import collections
2import inspect
3import io
4import sys
5
6py27 = sys.version_info >= (2, 7)
7py2k = sys.version_info.major < 3
8py3k = sys.version_info.major >= 3
9py35 = sys.version_info >= (3, 5)
10py36 = sys.version_info >= (3, 6)
11
12
13ArgSpec = collections.namedtuple(
14    "ArgSpec", ["args", "varargs", "keywords", "defaults"]
15)
16
17
18def inspect_getargspec(func):
19    """getargspec based on fully vendored getfullargspec from Python 3.3."""
20
21    if inspect.ismethod(func):
22        func = func.__func__
23    if not inspect.isfunction(func):
24        raise TypeError("{!r} is not a Python function".format(func))
25
26    co = func.__code__
27    if not inspect.iscode(co):
28        raise TypeError("{!r} is not a code object".format(co))
29
30    nargs = co.co_argcount
31    names = co.co_varnames
32    nkwargs = co.co_kwonlyargcount if py3k else 0
33    args = list(names[:nargs])
34
35    nargs += nkwargs
36    varargs = None
37    if co.co_flags & inspect.CO_VARARGS:
38        varargs = co.co_varnames[nargs]
39        nargs = nargs + 1
40    varkw = None
41    if co.co_flags & inspect.CO_VARKEYWORDS:
42        varkw = co.co_varnames[nargs]
43
44    return ArgSpec(args, varargs, varkw, func.__defaults__)
45
46
47if py3k:
48    from io import StringIO
49else:
50    # accepts strings
51    from StringIO import StringIO  # noqa
52
53if py3k:
54    import builtins as compat_builtins
55
56    string_types = (str,)
57    binary_type = bytes
58    text_type = str
59
60    def callable(fn):  # noqa
61        return hasattr(fn, "__call__")
62
63    def u(s):
64        return s
65
66    def ue(s):
67        return s
68
69    range = range  # noqa
70else:
71    import __builtin__ as compat_builtins
72
73    string_types = (basestring,)  # noqa
74    binary_type = str
75    text_type = unicode  # noqa
76    callable = callable  # noqa
77
78    def u(s):
79        return unicode(s, "utf-8")  # noqa
80
81    def ue(s):
82        return unicode(s, "unicode_escape")  # noqa
83
84    range = xrange  # noqa
85
86if py3k:
87    import collections.abc as collections_abc
88else:
89    import collections as collections_abc  # noqa
90
91if py35:
92
93    def _formatannotation(annotation, base_module=None):
94        """vendored from python 3.7
95        """
96
97        if getattr(annotation, "__module__", None) == "typing":
98            return repr(annotation).replace("typing.", "")
99        if isinstance(annotation, type):
100            if annotation.__module__ in ("builtins", base_module):
101                return annotation.__qualname__
102            return annotation.__module__ + "." + annotation.__qualname__
103        return repr(annotation)
104
105    def inspect_formatargspec(
106        args,
107        varargs=None,
108        varkw=None,
109        defaults=None,
110        kwonlyargs=(),
111        kwonlydefaults={},
112        annotations={},
113        formatarg=str,
114        formatvarargs=lambda name: "*" + name,
115        formatvarkw=lambda name: "**" + name,
116        formatvalue=lambda value: "=" + repr(value),
117        formatreturns=lambda text: " -> " + text,
118        formatannotation=_formatannotation,
119    ):
120        """Copy formatargspec from python 3.7 standard library.
121
122        Python 3 has deprecated formatargspec and requested that Signature
123        be used instead, however this requires a full reimplementation
124        of formatargspec() in terms of creating Parameter objects and such.
125        Instead of introducing all the object-creation overhead and having
126        to reinvent from scratch, just copy their compatibility routine.
127
128        """
129
130        def formatargandannotation(arg):
131            result = formatarg(arg)
132            if arg in annotations:
133                result += ": " + formatannotation(annotations[arg])
134            return result
135
136        specs = []
137        if defaults:
138            firstdefault = len(args) - len(defaults)
139        for i, arg in enumerate(args):
140            spec = formatargandannotation(arg)
141            if defaults and i >= firstdefault:
142                spec = spec + formatvalue(defaults[i - firstdefault])
143            specs.append(spec)
144        if varargs is not None:
145            specs.append(formatvarargs(formatargandannotation(varargs)))
146        else:
147            if kwonlyargs:
148                specs.append("*")
149        if kwonlyargs:
150            for kwonlyarg in kwonlyargs:
151                spec = formatargandannotation(kwonlyarg)
152                if kwonlydefaults and kwonlyarg in kwonlydefaults:
153                    spec += formatvalue(kwonlydefaults[kwonlyarg])
154                specs.append(spec)
155        if varkw is not None:
156            specs.append(formatvarkw(formatargandannotation(varkw)))
157        result = "(" + ", ".join(specs) + ")"
158        if "return" in annotations:
159            result += formatreturns(formatannotation(annotations["return"]))
160        return result
161
162
163else:
164    from inspect import formatargspec as inspect_formatargspec  # noqa
165
166
167if py3k:
168    from configparser import ConfigParser as SafeConfigParser
169    import configparser
170else:
171    from ConfigParser import SafeConfigParser  # noqa
172    import ConfigParser as configparser  # noqa
173
174if py2k:
175    from mako.util import parse_encoding
176
177if py35:
178    import importlib.util
179    import importlib.machinery
180
181    def load_module_py(module_id, path):
182        spec = importlib.util.spec_from_file_location(module_id, path)
183        module = importlib.util.module_from_spec(spec)
184        spec.loader.exec_module(module)
185        return module
186
187    def load_module_pyc(module_id, path):
188        spec = importlib.util.spec_from_file_location(module_id, path)
189        module = importlib.util.module_from_spec(spec)
190        spec.loader.exec_module(module)
191        return module
192
193
194elif py3k:
195    import importlib.machinery
196
197    def load_module_py(module_id, path):
198        module = importlib.machinery.SourceFileLoader(
199            module_id, path
200        ).load_module(module_id)
201        del sys.modules[module_id]
202        return module
203
204    def load_module_pyc(module_id, path):
205        module = importlib.machinery.SourcelessFileLoader(
206            module_id, path
207        ).load_module(module_id)
208        del sys.modules[module_id]
209        return module
210
211
212if py3k:
213
214    def get_bytecode_suffixes():
215        try:
216            return importlib.machinery.BYTECODE_SUFFIXES
217        except AttributeError:
218            return importlib.machinery.DEBUG_BYTECODE_SUFFIXES
219
220    def get_current_bytecode_suffixes():
221        if py35:
222            suffixes = importlib.machinery.BYTECODE_SUFFIXES
223        else:
224            if sys.flags.optimize:
225                suffixes = importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES
226            else:
227                suffixes = importlib.machinery.BYTECODE_SUFFIXES
228
229        return suffixes
230
231    def has_pep3147():
232
233        if py35:
234            return True
235        else:
236            # TODO: not sure if we are supporting old versions of Python
237            # the import here emits a deprecation warning which the test
238            # suite only catches if imp wasn't imported alreadt
239            # http://www.python.org/dev/peps/pep-3147/#detecting-pep-3147-availability
240            import imp
241
242            return hasattr(imp, "get_tag")
243
244
245else:
246    import imp
247
248    def load_module_py(module_id, path):  # noqa
249        with open(path, "rb") as fp:
250            mod = imp.load_source(module_id, path, fp)
251            if py2k:
252                source_encoding = parse_encoding(fp)
253                if source_encoding:
254                    mod._alembic_source_encoding = source_encoding
255            del sys.modules[module_id]
256            return mod
257
258    def load_module_pyc(module_id, path):  # noqa
259        with open(path, "rb") as fp:
260            mod = imp.load_compiled(module_id, path, fp)
261            # no source encoding here
262            del sys.modules[module_id]
263            return mod
264
265    def get_current_bytecode_suffixes():
266        if sys.flags.optimize:
267            return [".pyo"]  # e.g. .pyo
268        else:
269            return [".pyc"]  # e.g. .pyc
270
271    def has_pep3147():
272        return False
273
274
275try:
276    exec_ = getattr(compat_builtins, "exec")
277except AttributeError:
278    # Python 2
279    def exec_(func_text, globals_, lcl):
280        exec("exec func_text in globals_, lcl")
281
282
283################################################
284# cross-compatible metaclass implementation
285# Copyright (c) 2010-2012 Benjamin Peterson
286
287
288def with_metaclass(meta, base=object):
289    """Create a base class with a metaclass."""
290    return meta("%sBase" % meta.__name__, (base,), {})
291
292
293################################################
294
295if py3k:
296
297    def reraise(tp, value, tb=None, cause=None):
298        if cause is not None:
299            value.__cause__ = cause
300        if value.__traceback__ is not tb:
301            raise value.with_traceback(tb)
302        raise value
303
304    def raise_from_cause(exception, exc_info=None):
305        if exc_info is None:
306            exc_info = sys.exc_info()
307        exc_type, exc_value, exc_tb = exc_info
308        reraise(type(exception), exception, tb=exc_tb, cause=exc_value)
309
310
311else:
312    exec(
313        "def reraise(tp, value, tb=None, cause=None):\n"
314        "    raise tp, value, tb\n"
315    )
316
317    def raise_from_cause(exception, exc_info=None):
318        # not as nice as that of Py3K, but at least preserves
319        # the code line where the issue occurred
320        if exc_info is None:
321            exc_info = sys.exc_info()
322        exc_type, exc_value, exc_tb = exc_info
323        reraise(type(exception), exception, tb=exc_tb)
324
325
326# produce a wrapper that allows encoded text to stream
327# into a given buffer, but doesn't close it.
328# not sure of a more idiomatic approach to this.
329class EncodedIO(io.TextIOWrapper):
330    def close(self):
331        pass
332
333
334if py2k:
335    # in Py2K, the io.* package is awkward because it does not
336    # easily wrap the file type (e.g. sys.stdout) and I can't
337    # figure out at all how to wrap StringIO.StringIO
338    # and also might be user specified too.  So create a full
339    # adapter.
340
341    class ActLikePy3kIO(object):
342
343        """Produce an object capable of wrapping either
344        sys.stdout (e.g. file) *or* StringIO.StringIO().
345
346        """
347
348        def _false(self):
349            return False
350
351        def _true(self):
352            return True
353
354        readable = seekable = _false
355        writable = _true
356        closed = False
357
358        def __init__(self, file_):
359            self.file_ = file_
360
361        def write(self, text):
362            return self.file_.write(text)
363
364        def flush(self):
365            return self.file_.flush()
366
367    class EncodedIO(EncodedIO):
368        def __init__(self, file_, encoding):
369            super(EncodedIO, self).__init__(
370                ActLikePy3kIO(file_), encoding=encoding
371            )
372