1# code stolen from "six"
2
3import sys
4import types
5
6# True if we are running on Python 3.
7PY3 = sys.version_info[0] == 3
8
9if PY3: # pragma: no cover
10    string_types = str,
11    integer_types = int,
12    class_types = type,
13    text_type = str
14    long = int
15else:
16    string_types = basestring,
17    integer_types = (int, long)
18    class_types = (type, types.ClassType)
19    text_type = unicode
20    long = long
21
22# TODO check if errors is ever used
23
24def text_(s, encoding='latin-1', errors='strict'):
25    if isinstance(s, bytes):
26        return s.decode(encoding, errors)
27    return s
28
29def bytes_(s, encoding='latin-1', errors='strict'):
30    if isinstance(s, text_type):
31        return s.encode(encoding, errors)
32    return s
33
34if PY3: # pragma: no cover
35    def native_(s, encoding='latin-1', errors='strict'):
36        if isinstance(s, text_type):
37            return s
38        return str(s, encoding, errors)
39else:
40    def native_(s, encoding='latin-1', errors='strict'):
41        if isinstance(s, text_type):
42            return s.encode(encoding, errors)
43        return str(s)
44
45try:
46    from queue import Queue, Empty
47except ImportError:
48    from Queue import Queue, Empty
49
50if PY3: # pragma: no cover
51    from urllib import parse
52    urlparse = parse
53    from urllib.parse import quote as url_quote
54    from urllib.parse import urlencode as url_encode, quote_plus
55    from urllib.request import urlopen as url_open
56else:
57    import urlparse
58    from urllib import quote_plus
59    from urllib import quote as url_quote
60    from urllib import unquote as url_unquote
61    from urllib import urlencode as url_encode
62    from urllib2 import urlopen as url_open
63
64if PY3: # pragma: no cover
65    def reraise(exc_info):
66        etype, exc, tb = exc_info
67        if exc.__traceback__ is not tb:
68            raise exc.with_traceback(tb)
69        raise exc
70else: # pragma: no cover
71    exec("def reraise(exc): raise exc[0], exc[1], exc[2]")
72
73
74if PY3: # pragma: no cover
75    def iteritems_(d):
76        return d.items()
77    def itervalues_(d):
78        return d.values()
79else:
80    def iteritems_(d):
81        return d.iteritems()
82    def itervalues_(d):
83        return d.itervalues()
84
85
86if PY3: # pragma: no cover
87    def unquote(string):
88        if not string:
89            return b''
90        res = string.split(b'%')
91        if len(res) != 1:
92            string = res[0]
93            for item in res[1:]:
94                try:
95                    string += bytes([int(item[:2], 16)]) + item[2:]
96                except ValueError:
97                    string += b'%' + item
98        return string
99
100    def url_unquote(s):
101        return unquote(s.encode('ascii')).decode('latin-1')
102
103    def parse_qsl_text(qs, encoding='utf-8'):
104        qs = qs.encode('latin-1')
105        qs = qs.replace(b'+', b' ')
106        pairs = [s2 for s1 in qs.split(b'&') for s2 in s1.split(b';') if s2]
107        for name_value in pairs:
108            nv = name_value.split(b'=', 1)
109            if len(nv) != 2:
110                nv.append('')
111            name = unquote(nv[0])
112            value = unquote(nv[1])
113            yield (name.decode(encoding), value.decode(encoding))
114
115else:
116    from urlparse import parse_qsl
117
118    def parse_qsl_text(qs, encoding='utf-8'):
119        qsl = parse_qsl(
120            qs,
121            keep_blank_values=True,
122            strict_parsing=False
123        )
124        for (x, y) in qsl:
125            yield (x.decode(encoding), y.decode(encoding))
126
127
128if PY3: # pragma no cover
129    from html import escape
130else:
131    from cgi import escape
132
133
134# We only need this on Python3 but the issue was fixed in Pytohn 3.4.4 and 3.5.
135if PY3 and sys.version_info[:3] < (3, 4, 4):  # pragma no cover
136    # Work around http://bugs.python.org/issue23801
137    import cgi
138    from cgi import FieldStorage as _cgi_FieldStorage
139
140    class cgi_FieldStorage(_cgi_FieldStorage):
141        # This is taken exactly from Python 3.5's cgi.py module, and patched
142        # with the patch from http://bugs.python.org/issue23801.
143        def read_multi(self, environ, keep_blank_values, strict_parsing):
144            """Internal: read a part that is itself multipart."""
145            ib = self.innerboundary
146            if not cgi.valid_boundary(ib):
147                raise ValueError(
148                    'Invalid boundary in multipart form: %r' % (ib,))
149            self.list = []
150            if self.qs_on_post:
151                query = cgi.urllib.parse.parse_qsl(
152                    self.qs_on_post, self.keep_blank_values,
153                    self.strict_parsing,
154                    encoding=self.encoding, errors=self.errors)
155                for key, value in query:
156                    self.list.append(cgi.MiniFieldStorage(key, value))
157
158            klass = self.FieldStorageClass or self.__class__
159            first_line = self.fp.readline()  # bytes
160            if not isinstance(first_line, bytes):
161                raise ValueError("%s should return bytes, got %s"
162                                 % (self.fp, type(first_line).__name__))
163            self.bytes_read += len(first_line)
164
165            # Ensure that we consume the file until we've hit our innerboundary
166            while (first_line.strip() != (b"--" + self.innerboundary) and
167                    first_line):
168                first_line = self.fp.readline()
169                self.bytes_read += len(first_line)
170
171            while True:
172                parser = cgi.FeedParser()
173                hdr_text = b""
174                while True:
175                    data = self.fp.readline()
176                    hdr_text += data
177                    if not data.strip():
178                        break
179                if not hdr_text:
180                    break
181                # parser takes strings, not bytes
182                self.bytes_read += len(hdr_text)
183                parser.feed(hdr_text.decode(self.encoding, self.errors))
184                headers = parser.close()
185                part = klass(self.fp, headers, ib, environ, keep_blank_values,
186                             strict_parsing, self.limit-self.bytes_read,
187                             self.encoding, self.errors)
188                self.bytes_read += part.bytes_read
189                self.list.append(part)
190                if part.done or self.bytes_read >= self.length > 0:
191                    break
192            self.skip_lines()
193else:
194    from cgi import FieldStorage as cgi_FieldStorage
195