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