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