Path: blob/master/venv/Lib/site-packages/requests/cookies.py
811 views
# -*- coding: utf-8 -*-12"""3requests.cookies4~~~~~~~~~~~~~~~~56Compatibility code to be able to use `cookielib.CookieJar` with requests.78requests.utils imports from here, so be careful with imports.9"""1011import copy12import time13import calendar1415from ._internal_utils import to_native_string16from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping1718try:19import threading20except ImportError:21import dummy_threading as threading222324class MockRequest(object):25"""Wraps a `requests.Request` to mimic a `urllib2.Request`.2627The code in `cookielib.CookieJar` expects this interface in order to correctly28manage cookie policies, i.e., determine whether a cookie can be set, given the29domains of the request and the cookie.3031The original request object is read-only. The client is responsible for collecting32the new headers via `get_new_headers()` and interpreting them appropriately. You33probably want `get_cookie_header`, defined below.34"""3536def __init__(self, request):37self._r = request38self._new_headers = {}39self.type = urlparse(self._r.url).scheme4041def get_type(self):42return self.type4344def get_host(self):45return urlparse(self._r.url).netloc4647def get_origin_req_host(self):48return self.get_host()4950def get_full_url(self):51# Only return the response's URL if the user hadn't set the Host52# header53if not self._r.headers.get('Host'):54return self._r.url55# If they did set it, retrieve it and reconstruct the expected domain56host = to_native_string(self._r.headers['Host'], encoding='utf-8')57parsed = urlparse(self._r.url)58# Reconstruct the URL as we expect it59return urlunparse([60parsed.scheme, host, parsed.path, parsed.params, parsed.query,61parsed.fragment62])6364def is_unverifiable(self):65return True6667def has_header(self, name):68return name in self._r.headers or name in self._new_headers6970def get_header(self, name, default=None):71return self._r.headers.get(name, self._new_headers.get(name, default))7273def add_header(self, key, val):74"""cookielib has no legitimate use for this method; add it back if you find one."""75raise NotImplementedError("Cookie headers should be added with add_unredirected_header()")7677def add_unredirected_header(self, name, value):78self._new_headers[name] = value7980def get_new_headers(self):81return self._new_headers8283@property84def unverifiable(self):85return self.is_unverifiable()8687@property88def origin_req_host(self):89return self.get_origin_req_host()9091@property92def host(self):93return self.get_host()949596class MockResponse(object):97"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.9899...what? Basically, expose the parsed HTTP headers from the server response100the way `cookielib` expects to see them.101"""102103def __init__(self, headers):104"""Make a MockResponse for `cookielib` to read.105106:param headers: a httplib.HTTPMessage or analogous carrying the headers107"""108self._headers = headers109110def info(self):111return self._headers112113def getheaders(self, name):114self._headers.getheaders(name)115116117def extract_cookies_to_jar(jar, request, response):118"""Extract the cookies from the response into a CookieJar.119120:param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar)121:param request: our own requests.Request object122:param response: urllib3.HTTPResponse object123"""124if not (hasattr(response, '_original_response') and125response._original_response):126return127# the _original_response field is the wrapped httplib.HTTPResponse object,128req = MockRequest(request)129# pull out the HTTPMessage with the headers and put it in the mock:130res = MockResponse(response._original_response.msg)131jar.extract_cookies(res, req)132133134def get_cookie_header(jar, request):135"""136Produce an appropriate Cookie header string to be sent with `request`, or None.137138:rtype: str139"""140r = MockRequest(request)141jar.add_cookie_header(r)142return r.get_new_headers().get('Cookie')143144145def remove_cookie_by_name(cookiejar, name, domain=None, path=None):146"""Unsets a cookie by name, by default over all domains and paths.147148Wraps CookieJar.clear(), is O(n).149"""150clearables = []151for cookie in cookiejar:152if cookie.name != name:153continue154if domain is not None and domain != cookie.domain:155continue156if path is not None and path != cookie.path:157continue158clearables.append((cookie.domain, cookie.path, cookie.name))159160for domain, path, name in clearables:161cookiejar.clear(domain, path, name)162163164class CookieConflictError(RuntimeError):165"""There are two cookies that meet the criteria specified in the cookie jar.166Use .get and .set and include domain and path args in order to be more specific.167"""168169170class RequestsCookieJar(cookielib.CookieJar, MutableMapping):171"""Compatibility class; is a cookielib.CookieJar, but exposes a dict172interface.173174This is the CookieJar we create by default for requests and sessions that175don't specify one, since some clients may expect response.cookies and176session.cookies to support dict operations.177178Requests does not use the dict interface internally; it's just for179compatibility with external client code. All requests code should work180out of the box with externally provided instances of ``CookieJar``, e.g.181``LWPCookieJar`` and ``FileCookieJar``.182183Unlike a regular CookieJar, this class is pickleable.184185.. warning:: dictionary operations that are normally O(1) may be O(n).186"""187188def get(self, name, default=None, domain=None, path=None):189"""Dict-like get() that also supports optional domain and path args in190order to resolve naming collisions from using one cookie jar over191multiple domains.192193.. warning:: operation is O(n), not O(1).194"""195try:196return self._find_no_duplicates(name, domain, path)197except KeyError:198return default199200def set(self, name, value, **kwargs):201"""Dict-like set() that also supports optional domain and path args in202order to resolve naming collisions from using one cookie jar over203multiple domains.204"""205# support client code that unsets cookies by assignment of a None value:206if value is None:207remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path'))208return209210if isinstance(value, Morsel):211c = morsel_to_cookie(value)212else:213c = create_cookie(name, value, **kwargs)214self.set_cookie(c)215return c216217def iterkeys(self):218"""Dict-like iterkeys() that returns an iterator of names of cookies219from the jar.220221.. seealso:: itervalues() and iteritems().222"""223for cookie in iter(self):224yield cookie.name225226def keys(self):227"""Dict-like keys() that returns a list of names of cookies from the228jar.229230.. seealso:: values() and items().231"""232return list(self.iterkeys())233234def itervalues(self):235"""Dict-like itervalues() that returns an iterator of values of cookies236from the jar.237238.. seealso:: iterkeys() and iteritems().239"""240for cookie in iter(self):241yield cookie.value242243def values(self):244"""Dict-like values() that returns a list of values of cookies from the245jar.246247.. seealso:: keys() and items().248"""249return list(self.itervalues())250251def iteritems(self):252"""Dict-like iteritems() that returns an iterator of name-value tuples253from the jar.254255.. seealso:: iterkeys() and itervalues().256"""257for cookie in iter(self):258yield cookie.name, cookie.value259260def items(self):261"""Dict-like items() that returns a list of name-value tuples from the262jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a263vanilla python dict of key value pairs.264265.. seealso:: keys() and values().266"""267return list(self.iteritems())268269def list_domains(self):270"""Utility method to list all the domains in the jar."""271domains = []272for cookie in iter(self):273if cookie.domain not in domains:274domains.append(cookie.domain)275return domains276277def list_paths(self):278"""Utility method to list all the paths in the jar."""279paths = []280for cookie in iter(self):281if cookie.path not in paths:282paths.append(cookie.path)283return paths284285def multiple_domains(self):286"""Returns True if there are multiple domains in the jar.287Returns False otherwise.288289:rtype: bool290"""291domains = []292for cookie in iter(self):293if cookie.domain is not None and cookie.domain in domains:294return True295domains.append(cookie.domain)296return False # there is only one domain in jar297298def get_dict(self, domain=None, path=None):299"""Takes as an argument an optional domain and path and returns a plain300old Python dict of name-value pairs of cookies that meet the301requirements.302303:rtype: dict304"""305dictionary = {}306for cookie in iter(self):307if (308(domain is None or cookie.domain == domain) and309(path is None or cookie.path == path)310):311dictionary[cookie.name] = cookie.value312return dictionary313314def __contains__(self, name):315try:316return super(RequestsCookieJar, self).__contains__(name)317except CookieConflictError:318return True319320def __getitem__(self, name):321"""Dict-like __getitem__() for compatibility with client code. Throws322exception if there are more than one cookie with name. In that case,323use the more explicit get() method instead.324325.. warning:: operation is O(n), not O(1).326"""327return self._find_no_duplicates(name)328329def __setitem__(self, name, value):330"""Dict-like __setitem__ for compatibility with client code. Throws331exception if there is already a cookie of that name in the jar. In that332case, use the more explicit set() method instead.333"""334self.set(name, value)335336def __delitem__(self, name):337"""Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s338``remove_cookie_by_name()``.339"""340remove_cookie_by_name(self, name)341342def set_cookie(self, cookie, *args, **kwargs):343if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'):344cookie.value = cookie.value.replace('\\"', '')345return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs)346347def update(self, other):348"""Updates this jar with cookies from another CookieJar or dict-like"""349if isinstance(other, cookielib.CookieJar):350for cookie in other:351self.set_cookie(copy.copy(cookie))352else:353super(RequestsCookieJar, self).update(other)354355def _find(self, name, domain=None, path=None):356"""Requests uses this method internally to get cookie values.357358If there are conflicting cookies, _find arbitrarily chooses one.359See _find_no_duplicates if you want an exception thrown if there are360conflicting cookies.361362:param name: a string containing name of cookie363:param domain: (optional) string containing domain of cookie364:param path: (optional) string containing path of cookie365:return: cookie.value366"""367for cookie in iter(self):368if cookie.name == name:369if domain is None or cookie.domain == domain:370if path is None or cookie.path == path:371return cookie.value372373raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))374375def _find_no_duplicates(self, name, domain=None, path=None):376"""Both ``__get_item__`` and ``get`` call this function: it's never377used elsewhere in Requests.378379:param name: a string containing name of cookie380:param domain: (optional) string containing domain of cookie381:param path: (optional) string containing path of cookie382:raises KeyError: if cookie is not found383:raises CookieConflictError: if there are multiple cookies384that match name and optionally domain and path385:return: cookie.value386"""387toReturn = None388for cookie in iter(self):389if cookie.name == name:390if domain is None or cookie.domain == domain:391if path is None or cookie.path == path:392if toReturn is not None: # if there are multiple cookies that meet passed in criteria393raise CookieConflictError('There are multiple cookies with name, %r' % (name))394toReturn = cookie.value # we will eventually return this as long as no cookie conflict395396if toReturn:397return toReturn398raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))399400def __getstate__(self):401"""Unlike a normal CookieJar, this class is pickleable."""402state = self.__dict__.copy()403# remove the unpickleable RLock object404state.pop('_cookies_lock')405return state406407def __setstate__(self, state):408"""Unlike a normal CookieJar, this class is pickleable."""409self.__dict__.update(state)410if '_cookies_lock' not in self.__dict__:411self._cookies_lock = threading.RLock()412413def copy(self):414"""Return a copy of this RequestsCookieJar."""415new_cj = RequestsCookieJar()416new_cj.set_policy(self.get_policy())417new_cj.update(self)418return new_cj419420def get_policy(self):421"""Return the CookiePolicy instance used."""422return self._policy423424425def _copy_cookie_jar(jar):426if jar is None:427return None428429if hasattr(jar, 'copy'):430# We're dealing with an instance of RequestsCookieJar431return jar.copy()432# We're dealing with a generic CookieJar instance433new_jar = copy.copy(jar)434new_jar.clear()435for cookie in jar:436new_jar.set_cookie(copy.copy(cookie))437return new_jar438439440def create_cookie(name, value, **kwargs):441"""Make a cookie from underspecified parameters.442443By default, the pair of `name` and `value` will be set for the domain ''444and sent on every request (this is sometimes called a "supercookie").445"""446result = {447'version': 0,448'name': name,449'value': value,450'port': None,451'domain': '',452'path': '/',453'secure': False,454'expires': None,455'discard': True,456'comment': None,457'comment_url': None,458'rest': {'HttpOnly': None},459'rfc2109': False,460}461462badargs = set(kwargs) - set(result)463if badargs:464err = 'create_cookie() got unexpected keyword arguments: %s'465raise TypeError(err % list(badargs))466467result.update(kwargs)468result['port_specified'] = bool(result['port'])469result['domain_specified'] = bool(result['domain'])470result['domain_initial_dot'] = result['domain'].startswith('.')471result['path_specified'] = bool(result['path'])472473return cookielib.Cookie(**result)474475476def morsel_to_cookie(morsel):477"""Convert a Morsel object into a Cookie containing the one k/v pair."""478479expires = None480if morsel['max-age']:481try:482expires = int(time.time() + int(morsel['max-age']))483except ValueError:484raise TypeError('max-age: %s must be integer' % morsel['max-age'])485elif morsel['expires']:486time_template = '%a, %d-%b-%Y %H:%M:%S GMT'487expires = calendar.timegm(488time.strptime(morsel['expires'], time_template)489)490return create_cookie(491comment=morsel['comment'],492comment_url=bool(morsel['comment']),493discard=False,494domain=morsel['domain'],495expires=expires,496name=morsel.key,497path=morsel['path'],498port=None,499rest={'HttpOnly': morsel['httponly']},500rfc2109=False,501secure=bool(morsel['secure']),502value=morsel.value,503version=morsel['version'] or 0,504)505506507def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):508"""Returns a CookieJar from a key/value dictionary.509510:param cookie_dict: Dict of key/values to insert into CookieJar.511:param cookiejar: (optional) A cookiejar to add the cookies to.512:param overwrite: (optional) If False, will not replace cookies513already in the jar with new ones.514:rtype: CookieJar515"""516if cookiejar is None:517cookiejar = RequestsCookieJar()518519if cookie_dict is not None:520names_from_jar = [cookie.name for cookie in cookiejar]521for name in cookie_dict:522if overwrite or (name not in names_from_jar):523cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))524525return cookiejar526527528def merge_cookies(cookiejar, cookies):529"""Add cookies to cookiejar and returns a merged CookieJar.530531:param cookiejar: CookieJar object to add the cookies to.532:param cookies: Dictionary or CookieJar object to be added.533:rtype: CookieJar534"""535if not isinstance(cookiejar, cookielib.CookieJar):536raise ValueError('You can only merge into CookieJar')537538if isinstance(cookies, dict):539cookiejar = cookiejar_from_dict(540cookies, cookiejar=cookiejar, overwrite=False)541elif isinstance(cookies, cookielib.CookieJar):542try:543cookiejar.update(cookies)544except AttributeError:545for cookie_in_jar in cookies:546cookiejar.set_cookie(cookie_in_jar)547548return cookiejar549550551