1import inspect
2import platform
3import sys
4import types
5
6WIN = platform.system() == 'Windows'
7
8try:  # pragma: no cover
9    import __pypy__
10    PYPY = True
11except:  # pragma: no cover
12    __pypy__ = None
13    PYPY = False
14
15try:
16    import cPickle as pickle
17except ImportError:  # pragma: no cover
18    import pickle
19
20# PY3 is left as bw-compat but PY2 should be used for most checks.
21PY2 = sys.version_info[0] == 2
22PY3 = sys.version_info[0] == 3
23
24if PY2:
25    string_types = basestring,
26    integer_types = (int, long)
27    class_types = (type, types.ClassType)
28    text_type = unicode
29    binary_type = str
30    long = long
31else:
32    string_types = str,
33    integer_types = int,
34    class_types = type,
35    text_type = str
36    binary_type = bytes
37    long = int
38
39def text_(s, encoding='latin-1', errors='strict'):
40    """ If ``s`` is an instance of ``binary_type``, return
41    ``s.decode(encoding, errors)``, otherwise return ``s``"""
42    if isinstance(s, binary_type):
43        return s.decode(encoding, errors)
44    return s
45
46def bytes_(s, encoding='latin-1', errors='strict'):
47    """ If ``s`` is an instance of ``text_type``, return
48    ``s.encode(encoding, errors)``, otherwise return ``s``"""
49    if isinstance(s, text_type):
50        return s.encode(encoding, errors)
51    return s
52
53if PY2:
54    def ascii_native_(s):
55        if isinstance(s, text_type):
56            s = s.encode('ascii')
57        return str(s)
58else:
59    def ascii_native_(s):
60        if isinstance(s, text_type):
61            s = s.encode('ascii')
62        return str(s, 'ascii', 'strict')
63
64ascii_native_.__doc__ = """
65Python 3: If ``s`` is an instance of ``text_type``, return
66``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')``
67
68Python 2: If ``s`` is an instance of ``text_type``, return
69``s.encode('ascii')``, otherwise return ``str(s)``
70"""
71
72
73if PY2:
74    def native_(s, encoding='latin-1', errors='strict'):
75        """ If ``s`` is an instance of ``text_type``, return
76        ``s.encode(encoding, errors)``, otherwise return ``str(s)``"""
77        if isinstance(s, text_type):
78            return s.encode(encoding, errors)
79        return str(s)
80else:
81    def native_(s, encoding='latin-1', errors='strict'):
82        """ If ``s`` is an instance of ``text_type``, return
83        ``s``, otherwise return ``str(s, encoding, errors)``"""
84        if isinstance(s, text_type):
85            return s
86        return str(s, encoding, errors)
87
88native_.__doc__ = """
89Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
90return ``str(s, encoding, errors)``
91
92Python 2: If ``s`` is an instance of ``text_type``, return
93``s.encode(encoding, errors)``, otherwise return ``str(s)``
94"""
95
96if PY2:
97    import urlparse
98    from urllib import quote as url_quote
99    from urllib import quote_plus as url_quote_plus
100    from urllib import unquote as url_unquote
101    from urllib import urlencode as url_encode
102    from urllib2 import urlopen as url_open
103
104    def url_unquote_text(v, encoding='utf-8', errors='replace'): # pragma: no cover
105        v = url_unquote(v)
106        return v.decode(encoding, errors)
107
108    def url_unquote_native(v, encoding='utf-8', errors='replace'): # pragma: no cover
109        return native_(url_unquote_text(v, encoding, errors))
110else:
111    from urllib import parse
112    urlparse = parse
113    from urllib.parse import quote as url_quote
114    from urllib.parse import quote_plus as url_quote_plus
115    from urllib.parse import unquote as url_unquote
116    from urllib.parse import urlencode as url_encode
117    from urllib.request import urlopen as url_open
118    url_unquote_text = url_unquote
119    url_unquote_native = url_unquote
120
121
122if PY2:  # pragma: no cover
123    def exec_(code, globs=None, locs=None):
124        """Execute code in a namespace."""
125        if globs is None:
126            frame = sys._getframe(1)
127            globs = frame.f_globals
128            if locs is None:
129                locs = frame.f_locals
130            del frame
131        elif locs is None:
132            locs = globs
133        exec("""exec code in globs, locs""")
134
135    exec_("""def reraise(tp, value, tb=None):
136    raise tp, value, tb
137""")
138
139else:  # pragma: no cover
140    import builtins
141    exec_ = getattr(builtins, "exec")
142
143    def reraise(tp, value, tb=None):
144        if value is None:
145            value = tp
146        if value.__traceback__ is not tb:
147            raise value.with_traceback(tb)
148        raise value
149
150    del builtins
151
152
153if PY2:  # pragma: no cover
154    def iteritems_(d):
155        return d.iteritems()
156
157    def itervalues_(d):
158        return d.itervalues()
159
160    def iterkeys_(d):
161        return d.iterkeys()
162else:  # pragma: no cover
163    def iteritems_(d):
164        return d.items()
165
166    def itervalues_(d):
167        return d.values()
168
169    def iterkeys_(d):
170        return d.keys()
171
172
173if PY2:
174    map_ = map
175else:
176    def map_(*arg):
177        return list(map(*arg))
178
179if PY2:
180    def is_nonstr_iter(v):
181        return hasattr(v, '__iter__')
182else:
183    def is_nonstr_iter(v):
184        if isinstance(v, str):
185            return False
186        return hasattr(v, '__iter__')
187
188if PY2:
189    im_func = 'im_func'
190    im_self = 'im_self'
191else:
192    im_func = '__func__'
193    im_self = '__self__'
194
195try:
196    import configparser
197except ImportError:
198    import ConfigParser as configparser
199
200try:
201    from http.cookies import SimpleCookie
202except ImportError:
203    from Cookie import SimpleCookie
204
205if PY2:
206    from cgi import escape
207else:
208    from html import escape
209
210if PY2:
211    input_ = raw_input
212else:
213    input_ = input
214
215if PY2:
216    from io import BytesIO as NativeIO
217else:
218    from io import StringIO as NativeIO
219
220# "json" is not an API; it's here to support older pyramid_debugtoolbar
221# versions which attempt to import it
222import json
223
224if PY2:
225    def decode_path_info(path):
226        return path.decode('utf-8')
227else:
228    # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before
229    # decoding it to utf-8
230    def decode_path_info(path):
231        return path.encode('latin-1').decode('utf-8')
232
233if PY2:
234    from urlparse import unquote as unquote_to_bytes
235
236    def unquote_bytes_to_wsgi(bytestring):
237        return unquote_to_bytes(bytestring)
238else:
239    # see PEP 3333 for why we decode the path to latin-1
240    from urllib.parse import unquote_to_bytes
241
242    def unquote_bytes_to_wsgi(bytestring):
243        return unquote_to_bytes(bytestring).decode('latin-1')
244
245
246def is_bound_method(ob):
247    return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None
248
249# support annotations and keyword-only arguments in PY3
250if PY2:
251    from inspect import getargspec
252else:
253    from inspect import getfullargspec as getargspec
254
255if PY2:
256    from itertools import izip_longest as zip_longest
257else:
258    from itertools import zip_longest
259
260def is_unbound_method(fn):
261    """
262    This consistently verifies that the callable is bound to a
263    class.
264    """
265    is_bound = is_bound_method(fn)
266
267    if not is_bound and inspect.isroutine(fn):
268        spec = getargspec(fn)
269        has_self = len(spec.args) > 0 and spec.args[0] == 'self'
270
271        if PY2 and inspect.ismethod(fn):
272            return True
273        elif inspect.isfunction(fn) and has_self:
274            return True
275
276    return False
277