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 |
