Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_vendor/requests/cookies.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:47:31 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:9e54283cc701 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 | |
3 """ | |
4 requests.cookies | |
5 ~~~~~~~~~~~~~~~~ | |
6 | |
7 Compatibility code to be able to use `cookielib.CookieJar` with requests. | |
8 | |
9 requests.utils imports from here, so be careful with imports. | |
10 """ | |
11 | |
12 import copy | |
13 import time | |
14 import calendar | |
15 | |
16 from ._internal_utils import to_native_string | |
17 from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping | |
18 | |
19 try: | |
20 import threading | |
21 except ImportError: | |
22 import dummy_threading as threading | |
23 | |
24 | |
25 class MockRequest(object): | |
26 """Wraps a `requests.Request` to mimic a `urllib2.Request`. | |
27 | |
28 The code in `cookielib.CookieJar` expects this interface in order to correctly | |
29 manage cookie policies, i.e., determine whether a cookie can be set, given the | |
30 domains of the request and the cookie. | |
31 | |
32 The original request object is read-only. The client is responsible for collecting | |
33 the new headers via `get_new_headers()` and interpreting them appropriately. You | |
34 probably want `get_cookie_header`, defined below. | |
35 """ | |
36 | |
37 def __init__(self, request): | |
38 self._r = request | |
39 self._new_headers = {} | |
40 self.type = urlparse(self._r.url).scheme | |
41 | |
42 def get_type(self): | |
43 return self.type | |
44 | |
45 def get_host(self): | |
46 return urlparse(self._r.url).netloc | |
47 | |
48 def get_origin_req_host(self): | |
49 return self.get_host() | |
50 | |
51 def get_full_url(self): | |
52 # Only return the response's URL if the user hadn't set the Host | |
53 # header | |
54 if not self._r.headers.get('Host'): | |
55 return self._r.url | |
56 # If they did set it, retrieve it and reconstruct the expected domain | |
57 host = to_native_string(self._r.headers['Host'], encoding='utf-8') | |
58 parsed = urlparse(self._r.url) | |
59 # Reconstruct the URL as we expect it | |
60 return urlunparse([ | |
61 parsed.scheme, host, parsed.path, parsed.params, parsed.query, | |
62 parsed.fragment | |
63 ]) | |
64 | |
65 def is_unverifiable(self): | |
66 return True | |
67 | |
68 def has_header(self, name): | |
69 return name in self._r.headers or name in self._new_headers | |
70 | |
71 def get_header(self, name, default=None): | |
72 return self._r.headers.get(name, self._new_headers.get(name, default)) | |
73 | |
74 def add_header(self, key, val): | |
75 """cookielib has no legitimate use for this method; add it back if you find one.""" | |
76 raise NotImplementedError("Cookie headers should be added with add_unredirected_header()") | |
77 | |
78 def add_unredirected_header(self, name, value): | |
79 self._new_headers[name] = value | |
80 | |
81 def get_new_headers(self): | |
82 return self._new_headers | |
83 | |
84 @property | |
85 def unverifiable(self): | |
86 return self.is_unverifiable() | |
87 | |
88 @property | |
89 def origin_req_host(self): | |
90 return self.get_origin_req_host() | |
91 | |
92 @property | |
93 def host(self): | |
94 return self.get_host() | |
95 | |
96 | |
97 class MockResponse(object): | |
98 """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. | |
99 | |
100 ...what? Basically, expose the parsed HTTP headers from the server response | |
101 the way `cookielib` expects to see them. | |
102 """ | |
103 | |
104 def __init__(self, headers): | |
105 """Make a MockResponse for `cookielib` to read. | |
106 | |
107 :param headers: a httplib.HTTPMessage or analogous carrying the headers | |
108 """ | |
109 self._headers = headers | |
110 | |
111 def info(self): | |
112 return self._headers | |
113 | |
114 def getheaders(self, name): | |
115 self._headers.getheaders(name) | |
116 | |
117 | |
118 def extract_cookies_to_jar(jar, request, response): | |
119 """Extract the cookies from the response into a CookieJar. | |
120 | |
121 :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) | |
122 :param request: our own requests.Request object | |
123 :param response: urllib3.HTTPResponse object | |
124 """ | |
125 if not (hasattr(response, '_original_response') and | |
126 response._original_response): | |
127 return | |
128 # the _original_response field is the wrapped httplib.HTTPResponse object, | |
129 req = MockRequest(request) | |
130 # pull out the HTTPMessage with the headers and put it in the mock: | |
131 res = MockResponse(response._original_response.msg) | |
132 jar.extract_cookies(res, req) | |
133 | |
134 | |
135 def get_cookie_header(jar, request): | |
136 """ | |
137 Produce an appropriate Cookie header string to be sent with `request`, or None. | |
138 | |
139 :rtype: str | |
140 """ | |
141 r = MockRequest(request) | |
142 jar.add_cookie_header(r) | |
143 return r.get_new_headers().get('Cookie') | |
144 | |
145 | |
146 def remove_cookie_by_name(cookiejar, name, domain=None, path=None): | |
147 """Unsets a cookie by name, by default over all domains and paths. | |
148 | |
149 Wraps CookieJar.clear(), is O(n). | |
150 """ | |
151 clearables = [] | |
152 for cookie in cookiejar: | |
153 if cookie.name != name: | |
154 continue | |
155 if domain is not None and domain != cookie.domain: | |
156 continue | |
157 if path is not None and path != cookie.path: | |
158 continue | |
159 clearables.append((cookie.domain, cookie.path, cookie.name)) | |
160 | |
161 for domain, path, name in clearables: | |
162 cookiejar.clear(domain, path, name) | |
163 | |
164 | |
165 class CookieConflictError(RuntimeError): | |
166 """There are two cookies that meet the criteria specified in the cookie jar. | |
167 Use .get and .set and include domain and path args in order to be more specific. | |
168 """ | |
169 | |
170 | |
171 class RequestsCookieJar(cookielib.CookieJar, MutableMapping): | |
172 """Compatibility class; is a cookielib.CookieJar, but exposes a dict | |
173 interface. | |
174 | |
175 This is the CookieJar we create by default for requests and sessions that | |
176 don't specify one, since some clients may expect response.cookies and | |
177 session.cookies to support dict operations. | |
178 | |
179 Requests does not use the dict interface internally; it's just for | |
180 compatibility with external client code. All requests code should work | |
181 out of the box with externally provided instances of ``CookieJar``, e.g. | |
182 ``LWPCookieJar`` and ``FileCookieJar``. | |
183 | |
184 Unlike a regular CookieJar, this class is pickleable. | |
185 | |
186 .. warning:: dictionary operations that are normally O(1) may be O(n). | |
187 """ | |
188 | |
189 def get(self, name, default=None, domain=None, path=None): | |
190 """Dict-like get() that also supports optional domain and path args in | |
191 order to resolve naming collisions from using one cookie jar over | |
192 multiple domains. | |
193 | |
194 .. warning:: operation is O(n), not O(1). | |
195 """ | |
196 try: | |
197 return self._find_no_duplicates(name, domain, path) | |
198 except KeyError: | |
199 return default | |
200 | |
201 def set(self, name, value, **kwargs): | |
202 """Dict-like set() that also supports optional domain and path args in | |
203 order to resolve naming collisions from using one cookie jar over | |
204 multiple domains. | |
205 """ | |
206 # support client code that unsets cookies by assignment of a None value: | |
207 if value is None: | |
208 remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path')) | |
209 return | |
210 | |
211 if isinstance(value, Morsel): | |
212 c = morsel_to_cookie(value) | |
213 else: | |
214 c = create_cookie(name, value, **kwargs) | |
215 self.set_cookie(c) | |
216 return c | |
217 | |
218 def iterkeys(self): | |
219 """Dict-like iterkeys() that returns an iterator of names of cookies | |
220 from the jar. | |
221 | |
222 .. seealso:: itervalues() and iteritems(). | |
223 """ | |
224 for cookie in iter(self): | |
225 yield cookie.name | |
226 | |
227 def keys(self): | |
228 """Dict-like keys() that returns a list of names of cookies from the | |
229 jar. | |
230 | |
231 .. seealso:: values() and items(). | |
232 """ | |
233 return list(self.iterkeys()) | |
234 | |
235 def itervalues(self): | |
236 """Dict-like itervalues() that returns an iterator of values of cookies | |
237 from the jar. | |
238 | |
239 .. seealso:: iterkeys() and iteritems(). | |
240 """ | |
241 for cookie in iter(self): | |
242 yield cookie.value | |
243 | |
244 def values(self): | |
245 """Dict-like values() that returns a list of values of cookies from the | |
246 jar. | |
247 | |
248 .. seealso:: keys() and items(). | |
249 """ | |
250 return list(self.itervalues()) | |
251 | |
252 def iteritems(self): | |
253 """Dict-like iteritems() that returns an iterator of name-value tuples | |
254 from the jar. | |
255 | |
256 .. seealso:: iterkeys() and itervalues(). | |
257 """ | |
258 for cookie in iter(self): | |
259 yield cookie.name, cookie.value | |
260 | |
261 def items(self): | |
262 """Dict-like items() that returns a list of name-value tuples from the | |
263 jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a | |
264 vanilla python dict of key value pairs. | |
265 | |
266 .. seealso:: keys() and values(). | |
267 """ | |
268 return list(self.iteritems()) | |
269 | |
270 def list_domains(self): | |
271 """Utility method to list all the domains in the jar.""" | |
272 domains = [] | |
273 for cookie in iter(self): | |
274 if cookie.domain not in domains: | |
275 domains.append(cookie.domain) | |
276 return domains | |
277 | |
278 def list_paths(self): | |
279 """Utility method to list all the paths in the jar.""" | |
280 paths = [] | |
281 for cookie in iter(self): | |
282 if cookie.path not in paths: | |
283 paths.append(cookie.path) | |
284 return paths | |
285 | |
286 def multiple_domains(self): | |
287 """Returns True if there are multiple domains in the jar. | |
288 Returns False otherwise. | |
289 | |
290 :rtype: bool | |
291 """ | |
292 domains = [] | |
293 for cookie in iter(self): | |
294 if cookie.domain is not None and cookie.domain in domains: | |
295 return True | |
296 domains.append(cookie.domain) | |
297 return False # there is only one domain in jar | |
298 | |
299 def get_dict(self, domain=None, path=None): | |
300 """Takes as an argument an optional domain and path and returns a plain | |
301 old Python dict of name-value pairs of cookies that meet the | |
302 requirements. | |
303 | |
304 :rtype: dict | |
305 """ | |
306 dictionary = {} | |
307 for cookie in iter(self): | |
308 if ( | |
309 (domain is None or cookie.domain == domain) and | |
310 (path is None or cookie.path == path) | |
311 ): | |
312 dictionary[cookie.name] = cookie.value | |
313 return dictionary | |
314 | |
315 def __contains__(self, name): | |
316 try: | |
317 return super(RequestsCookieJar, self).__contains__(name) | |
318 except CookieConflictError: | |
319 return True | |
320 | |
321 def __getitem__(self, name): | |
322 """Dict-like __getitem__() for compatibility with client code. Throws | |
323 exception if there are more than one cookie with name. In that case, | |
324 use the more explicit get() method instead. | |
325 | |
326 .. warning:: operation is O(n), not O(1). | |
327 """ | |
328 return self._find_no_duplicates(name) | |
329 | |
330 def __setitem__(self, name, value): | |
331 """Dict-like __setitem__ for compatibility with client code. Throws | |
332 exception if there is already a cookie of that name in the jar. In that | |
333 case, use the more explicit set() method instead. | |
334 """ | |
335 self.set(name, value) | |
336 | |
337 def __delitem__(self, name): | |
338 """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s | |
339 ``remove_cookie_by_name()``. | |
340 """ | |
341 remove_cookie_by_name(self, name) | |
342 | |
343 def set_cookie(self, cookie, *args, **kwargs): | |
344 if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'): | |
345 cookie.value = cookie.value.replace('\\"', '') | |
346 return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) | |
347 | |
348 def update(self, other): | |
349 """Updates this jar with cookies from another CookieJar or dict-like""" | |
350 if isinstance(other, cookielib.CookieJar): | |
351 for cookie in other: | |
352 self.set_cookie(copy.copy(cookie)) | |
353 else: | |
354 super(RequestsCookieJar, self).update(other) | |
355 | |
356 def _find(self, name, domain=None, path=None): | |
357 """Requests uses this method internally to get cookie values. | |
358 | |
359 If there are conflicting cookies, _find arbitrarily chooses one. | |
360 See _find_no_duplicates if you want an exception thrown if there are | |
361 conflicting cookies. | |
362 | |
363 :param name: a string containing name of cookie | |
364 :param domain: (optional) string containing domain of cookie | |
365 :param path: (optional) string containing path of cookie | |
366 :return: cookie.value | |
367 """ | |
368 for cookie in iter(self): | |
369 if cookie.name == name: | |
370 if domain is None or cookie.domain == domain: | |
371 if path is None or cookie.path == path: | |
372 return cookie.value | |
373 | |
374 raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) | |
375 | |
376 def _find_no_duplicates(self, name, domain=None, path=None): | |
377 """Both ``__get_item__`` and ``get`` call this function: it's never | |
378 used elsewhere in Requests. | |
379 | |
380 :param name: a string containing name of cookie | |
381 :param domain: (optional) string containing domain of cookie | |
382 :param path: (optional) string containing path of cookie | |
383 :raises KeyError: if cookie is not found | |
384 :raises CookieConflictError: if there are multiple cookies | |
385 that match name and optionally domain and path | |
386 :return: cookie.value | |
387 """ | |
388 toReturn = None | |
389 for cookie in iter(self): | |
390 if cookie.name == name: | |
391 if domain is None or cookie.domain == domain: | |
392 if path is None or cookie.path == path: | |
393 if toReturn is not None: # if there are multiple cookies that meet passed in criteria | |
394 raise CookieConflictError('There are multiple cookies with name, %r' % (name)) | |
395 toReturn = cookie.value # we will eventually return this as long as no cookie conflict | |
396 | |
397 if toReturn: | |
398 return toReturn | |
399 raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) | |
400 | |
401 def __getstate__(self): | |
402 """Unlike a normal CookieJar, this class is pickleable.""" | |
403 state = self.__dict__.copy() | |
404 # remove the unpickleable RLock object | |
405 state.pop('_cookies_lock') | |
406 return state | |
407 | |
408 def __setstate__(self, state): | |
409 """Unlike a normal CookieJar, this class is pickleable.""" | |
410 self.__dict__.update(state) | |
411 if '_cookies_lock' not in self.__dict__: | |
412 self._cookies_lock = threading.RLock() | |
413 | |
414 def copy(self): | |
415 """Return a copy of this RequestsCookieJar.""" | |
416 new_cj = RequestsCookieJar() | |
417 new_cj.set_policy(self.get_policy()) | |
418 new_cj.update(self) | |
419 return new_cj | |
420 | |
421 def get_policy(self): | |
422 """Return the CookiePolicy instance used.""" | |
423 return self._policy | |
424 | |
425 | |
426 def _copy_cookie_jar(jar): | |
427 if jar is None: | |
428 return None | |
429 | |
430 if hasattr(jar, 'copy'): | |
431 # We're dealing with an instance of RequestsCookieJar | |
432 return jar.copy() | |
433 # We're dealing with a generic CookieJar instance | |
434 new_jar = copy.copy(jar) | |
435 new_jar.clear() | |
436 for cookie in jar: | |
437 new_jar.set_cookie(copy.copy(cookie)) | |
438 return new_jar | |
439 | |
440 | |
441 def create_cookie(name, value, **kwargs): | |
442 """Make a cookie from underspecified parameters. | |
443 | |
444 By default, the pair of `name` and `value` will be set for the domain '' | |
445 and sent on every request (this is sometimes called a "supercookie"). | |
446 """ | |
447 result = { | |
448 'version': 0, | |
449 'name': name, | |
450 'value': value, | |
451 'port': None, | |
452 'domain': '', | |
453 'path': '/', | |
454 'secure': False, | |
455 'expires': None, | |
456 'discard': True, | |
457 'comment': None, | |
458 'comment_url': None, | |
459 'rest': {'HttpOnly': None}, | |
460 'rfc2109': False, | |
461 } | |
462 | |
463 badargs = set(kwargs) - set(result) | |
464 if badargs: | |
465 err = 'create_cookie() got unexpected keyword arguments: %s' | |
466 raise TypeError(err % list(badargs)) | |
467 | |
468 result.update(kwargs) | |
469 result['port_specified'] = bool(result['port']) | |
470 result['domain_specified'] = bool(result['domain']) | |
471 result['domain_initial_dot'] = result['domain'].startswith('.') | |
472 result['path_specified'] = bool(result['path']) | |
473 | |
474 return cookielib.Cookie(**result) | |
475 | |
476 | |
477 def morsel_to_cookie(morsel): | |
478 """Convert a Morsel object into a Cookie containing the one k/v pair.""" | |
479 | |
480 expires = None | |
481 if morsel['max-age']: | |
482 try: | |
483 expires = int(time.time() + int(morsel['max-age'])) | |
484 except ValueError: | |
485 raise TypeError('max-age: %s must be integer' % morsel['max-age']) | |
486 elif morsel['expires']: | |
487 time_template = '%a, %d-%b-%Y %H:%M:%S GMT' | |
488 expires = calendar.timegm( | |
489 time.strptime(morsel['expires'], time_template) | |
490 ) | |
491 return create_cookie( | |
492 comment=morsel['comment'], | |
493 comment_url=bool(morsel['comment']), | |
494 discard=False, | |
495 domain=morsel['domain'], | |
496 expires=expires, | |
497 name=morsel.key, | |
498 path=morsel['path'], | |
499 port=None, | |
500 rest={'HttpOnly': morsel['httponly']}, | |
501 rfc2109=False, | |
502 secure=bool(morsel['secure']), | |
503 value=morsel.value, | |
504 version=morsel['version'] or 0, | |
505 ) | |
506 | |
507 | |
508 def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): | |
509 """Returns a CookieJar from a key/value dictionary. | |
510 | |
511 :param cookie_dict: Dict of key/values to insert into CookieJar. | |
512 :param cookiejar: (optional) A cookiejar to add the cookies to. | |
513 :param overwrite: (optional) If False, will not replace cookies | |
514 already in the jar with new ones. | |
515 :rtype: CookieJar | |
516 """ | |
517 if cookiejar is None: | |
518 cookiejar = RequestsCookieJar() | |
519 | |
520 if cookie_dict is not None: | |
521 names_from_jar = [cookie.name for cookie in cookiejar] | |
522 for name in cookie_dict: | |
523 if overwrite or (name not in names_from_jar): | |
524 cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) | |
525 | |
526 return cookiejar | |
527 | |
528 | |
529 def merge_cookies(cookiejar, cookies): | |
530 """Add cookies to cookiejar and returns a merged CookieJar. | |
531 | |
532 :param cookiejar: CookieJar object to add the cookies to. | |
533 :param cookies: Dictionary or CookieJar object to be added. | |
534 :rtype: CookieJar | |
535 """ | |
536 if not isinstance(cookiejar, cookielib.CookieJar): | |
537 raise ValueError('You can only merge into CookieJar') | |
538 | |
539 if isinstance(cookies, dict): | |
540 cookiejar = cookiejar_from_dict( | |
541 cookies, cookiejar=cookiejar, overwrite=False) | |
542 elif isinstance(cookies, cookielib.CookieJar): | |
543 try: | |
544 cookiejar.update(cookies) | |
545 except AttributeError: | |
546 for cookie_in_jar in cookies: | |
547 cookiejar.set_cookie(cookie_in_jar) | |
548 | |
549 return cookiejar |