comparison env/lib/python3.7/site-packages/requests_toolbelt/_compat.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
comparison
equal deleted inserted replaced
4:79f47841a781 5:9b1c78e6ba9c
1 """Private module full of compatibility hacks.
2
3 Primarily this is for downstream redistributions of requests that unvendor
4 urllib3 without providing a shim.
5
6 .. warning::
7
8 This module is private. If you use it, and something breaks, you were
9 warned
10 """
11 import sys
12
13 import requests
14
15 try:
16 from requests.packages.urllib3 import fields
17 from requests.packages.urllib3 import filepost
18 from requests.packages.urllib3 import poolmanager
19 except ImportError:
20 from urllib3 import fields
21 from urllib3 import filepost
22 from urllib3 import poolmanager
23
24 try:
25 from requests.packages.urllib3.connection import HTTPConnection
26 from requests.packages.urllib3 import connection
27 except ImportError:
28 try:
29 from urllib3.connection import HTTPConnection
30 from urllib3 import connection
31 except ImportError:
32 HTTPConnection = None
33 connection = None
34
35
36 if requests.__build__ < 0x020300:
37 timeout = None
38 else:
39 try:
40 from requests.packages.urllib3.util import timeout
41 except ImportError:
42 from urllib3.util import timeout
43
44 if requests.__build__ < 0x021000:
45 gaecontrib = None
46 else:
47 try:
48 from requests.packages.urllib3.contrib import appengine as gaecontrib
49 except ImportError:
50 from urllib3.contrib import appengine as gaecontrib
51
52 if requests.__build__ < 0x021200:
53 PyOpenSSLContext = None
54 else:
55 try:
56 from requests.packages.urllib3.contrib.pyopenssl \
57 import PyOpenSSLContext
58 except ImportError:
59 try:
60 from urllib3.contrib.pyopenssl import PyOpenSSLContext
61 except ImportError:
62 PyOpenSSLContext = None
63
64 PY3 = sys.version_info > (3, 0)
65
66 if PY3:
67 from collections.abc import Mapping, MutableMapping
68 import queue
69 from urllib.parse import urlencode, urljoin
70 else:
71 from collections import Mapping, MutableMapping
72 import Queue as queue
73 from urllib import urlencode
74 from urlparse import urljoin
75
76 try:
77 basestring = basestring
78 except NameError:
79 basestring = (str, bytes)
80
81
82 class HTTPHeaderDict(MutableMapping):
83 """
84 :param headers:
85 An iterable of field-value pairs. Must not contain multiple field names
86 when compared case-insensitively.
87
88 :param kwargs:
89 Additional field-value pairs to pass in to ``dict.update``.
90
91 A ``dict`` like container for storing HTTP Headers.
92
93 Field names are stored and compared case-insensitively in compliance with
94 RFC 7230. Iteration provides the first case-sensitive key seen for each
95 case-insensitive pair.
96
97 Using ``__setitem__`` syntax overwrites fields that compare equal
98 case-insensitively in order to maintain ``dict``'s api. For fields that
99 compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add``
100 in a loop.
101
102 If multiple fields that are equal case-insensitively are passed to the
103 constructor or ``.update``, the behavior is undefined and some will be
104 lost.
105
106 >>> headers = HTTPHeaderDict()
107 >>> headers.add('Set-Cookie', 'foo=bar')
108 >>> headers.add('set-cookie', 'baz=quxx')
109 >>> headers['content-length'] = '7'
110 >>> headers['SET-cookie']
111 'foo=bar, baz=quxx'
112 >>> headers['Content-Length']
113 '7'
114 """
115
116 def __init__(self, headers=None, **kwargs):
117 super(HTTPHeaderDict, self).__init__()
118 self._container = {}
119 if headers is not None:
120 if isinstance(headers, HTTPHeaderDict):
121 self._copy_from(headers)
122 else:
123 self.extend(headers)
124 if kwargs:
125 self.extend(kwargs)
126
127 def __setitem__(self, key, val):
128 self._container[key.lower()] = (key, val)
129 return self._container[key.lower()]
130
131 def __getitem__(self, key):
132 val = self._container[key.lower()]
133 return ', '.join(val[1:])
134
135 def __delitem__(self, key):
136 del self._container[key.lower()]
137
138 def __contains__(self, key):
139 return key.lower() in self._container
140
141 def __eq__(self, other):
142 if not isinstance(other, Mapping) and not hasattr(other, 'keys'):
143 return False
144 if not isinstance(other, type(self)):
145 other = type(self)(other)
146 return (dict((k.lower(), v) for k, v in self.itermerged()) ==
147 dict((k.lower(), v) for k, v in other.itermerged()))
148
149 def __ne__(self, other):
150 return not self.__eq__(other)
151
152 if not PY3: # Python 2
153 iterkeys = MutableMapping.iterkeys
154 itervalues = MutableMapping.itervalues
155
156 __marker = object()
157
158 def __len__(self):
159 return len(self._container)
160
161 def __iter__(self):
162 # Only provide the originally cased names
163 for vals in self._container.values():
164 yield vals[0]
165
166 def pop(self, key, default=__marker):
167 """D.pop(k[,d]) -> v, remove specified key and return its value.
168
169 If key is not found, d is returned if given, otherwise KeyError is
170 raised.
171 """
172 # Using the MutableMapping function directly fails due to the private
173 # marker.
174 # Using ordinary dict.pop would expose the internal structures.
175 # So let's reinvent the wheel.
176 try:
177 value = self[key]
178 except KeyError:
179 if default is self.__marker:
180 raise
181 return default
182 else:
183 del self[key]
184 return value
185
186 def discard(self, key):
187 try:
188 del self[key]
189 except KeyError:
190 pass
191
192 def add(self, key, val):
193 """Adds a (name, value) pair, doesn't overwrite the value if it already
194 exists.
195
196 >>> headers = HTTPHeaderDict(foo='bar')
197 >>> headers.add('Foo', 'baz')
198 >>> headers['foo']
199 'bar, baz'
200 """
201 key_lower = key.lower()
202 new_vals = key, val
203 # Keep the common case aka no item present as fast as possible
204 vals = self._container.setdefault(key_lower, new_vals)
205 if new_vals is not vals:
206 # new_vals was not inserted, as there was a previous one
207 if isinstance(vals, list):
208 # If already several items got inserted, we have a list
209 vals.append(val)
210 else:
211 # vals should be a tuple then, i.e. only one item so far
212 # Need to convert the tuple to list for further extension
213 self._container[key_lower] = [vals[0], vals[1], val]
214
215 def extend(self, *args, **kwargs):
216 """Generic import function for any type of header-like object.
217 Adapted version of MutableMapping.update in order to insert items
218 with self.add instead of self.__setitem__
219 """
220 if len(args) > 1:
221 raise TypeError("extend() takes at most 1 positional "
222 "arguments ({} given)".format(len(args)))
223 other = args[0] if len(args) >= 1 else ()
224
225 if isinstance(other, HTTPHeaderDict):
226 for key, val in other.iteritems():
227 self.add(key, val)
228 elif isinstance(other, Mapping):
229 for key in other:
230 self.add(key, other[key])
231 elif hasattr(other, "keys"):
232 for key in other.keys():
233 self.add(key, other[key])
234 else:
235 for key, value in other:
236 self.add(key, value)
237
238 for key, value in kwargs.items():
239 self.add(key, value)
240
241 def getlist(self, key):
242 """Returns a list of all the values for the named field. Returns an
243 empty list if the key doesn't exist."""
244 try:
245 vals = self._container[key.lower()]
246 except KeyError:
247 return []
248 else:
249 if isinstance(vals, tuple):
250 return [vals[1]]
251 else:
252 return vals[1:]
253
254 # Backwards compatibility for httplib
255 getheaders = getlist
256 getallmatchingheaders = getlist
257 iget = getlist
258
259 def __repr__(self):
260 return "%s(%s)" % (type(self).__name__, dict(self.itermerged()))
261
262 def _copy_from(self, other):
263 for key in other:
264 val = other.getlist(key)
265 if isinstance(val, list):
266 # Don't need to convert tuples
267 val = list(val)
268 self._container[key.lower()] = [key] + val
269
270 def copy(self):
271 clone = type(self)()
272 clone._copy_from(self)
273 return clone
274
275 def iteritems(self):
276 """Iterate over all header lines, including duplicate ones."""
277 for key in self:
278 vals = self._container[key.lower()]
279 for val in vals[1:]:
280 yield vals[0], val
281
282 def itermerged(self):
283 """Iterate over all headers, merging duplicate ones together."""
284 for key in self:
285 val = self._container[key.lower()]
286 yield val[0], ', '.join(val[1:])
287
288 def items(self):
289 return list(self.iteritems())
290
291 @classmethod
292 def from_httplib(cls, message): # Python 2
293 """Read headers from a Python 2 httplib message object."""
294 # python2.7 does not expose a proper API for exporting multiheaders
295 # efficiently. This function re-reads raw lines from the message
296 # object and extracts the multiheaders properly.
297 headers = []
298
299 for line in message.headers:
300 if line.startswith((' ', '\t')):
301 key, value = headers[-1]
302 headers[-1] = (key, value + '\r\n' + line.rstrip())
303 continue
304
305 key, value = line.split(':', 1)
306 headers.append((key, value.strip()))
307
308 return cls(headers)
309
310
311 __all__ = (
312 'basestring',
313 'connection',
314 'fields',
315 'filepost',
316 'poolmanager',
317 'timeout',
318 'HTTPHeaderDict',
319 'queue',
320 'urlencode',
321 'gaecontrib',
322 'urljoin',
323 'PyOpenSSLContext',
324 )