summaryrefslogtreecommitdiff
path: root/requests-0.14.0/requests/cookies.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests-0.14.0/requests/cookies.py')
-rw-r--r--requests-0.14.0/requests/cookies.py373
1 files changed, 373 insertions, 0 deletions
diff --git a/requests-0.14.0/requests/cookies.py b/requests-0.14.0/requests/cookies.py
new file mode 100644
index 0000000..bd2d665
--- /dev/null
+++ b/requests-0.14.0/requests/cookies.py
@@ -0,0 +1,373 @@
+"""
+Compatibility code to be able to use `cookielib.CookieJar` with requests.
+
+requests.utils imports from here, so be careful with imports.
+"""
+
+import collections
+from .compat import cookielib, urlparse, Morsel
+
+try:
+ import threading
+ # grr, pyflakes: this fixes "redefinition of unused 'threading'"
+ threading
+except ImportError:
+ import dummy_threading as threading
+
+
+class MockRequest(object):
+ """Wraps a `requests.Request` to mimic a `urllib2.Request`.
+
+ The code in `cookielib.CookieJar` expects this interface in order to correctly
+ manage cookie policies, i.e., determine whether a cookie can be set, given the
+ domains of the request and the cookie.
+
+ The original request object is read-only. The client is responsible for collecting
+ the new headers via `get_new_headers()` and interpreting them appropriately. You
+ probably want `get_cookie_header`, defined below.
+ """
+
+ def __init__(self, request):
+ self._r = request
+ self._new_headers = {}
+
+ def get_type(self):
+ return urlparse(self._r.full_url).scheme
+
+ def get_host(self):
+ return urlparse(self._r.full_url).netloc
+
+ def get_origin_req_host(self):
+ if self._r.response.history:
+ r = self._r.response.history[0]
+ return urlparse(r.url).netloc
+ else:
+ return self.get_host()
+
+ def get_full_url(self):
+ return self._r.full_url
+
+ def is_unverifiable(self):
+ # unverifiable == redirected
+ return bool(self._r.response.history)
+
+ def has_header(self, name):
+ return name in self._r.headers or name in self._new_headers
+
+ def get_header(self, name, default=None):
+ return self._r.headers.get(name, self._new_headers.get(name, default))
+
+ def add_header(self, key, val):
+ """cookielib has no legitimate use for this method; add it back if you find one."""
+ raise NotImplementedError("Cookie headers should be added with add_unredirected_header()")
+
+ def add_unredirected_header(self, name, value):
+ self._new_headers[name] = value
+
+ def get_new_headers(self):
+ return self._new_headers
+
+
+class MockResponse(object):
+ """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
+
+ ...what? Basically, expose the parsed HTTP headers from the server response
+ the way `cookielib` expects to see them.
+ """
+
+ def __init__(self, headers):
+ """Make a MockResponse for `cookielib` to read.
+
+ :param headers: a httplib.HTTPMessage or analogous carrying the headers
+ """
+ self._headers = headers
+
+ def info(self):
+ return self._headers
+
+ def getheaders(self, name):
+ self._headers.getheaders(name)
+
+
+def extract_cookies_to_jar(jar, request, response):
+ """Extract the cookies from the response into a CookieJar.
+
+ :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar)
+ :param request: our own requests.Request object
+ :param response: urllib3.HTTPResponse object
+ """
+ # the _original_response field is the wrapped httplib.HTTPResponse object,
+ req = MockRequest(request)
+ # pull out the HTTPMessage with the headers and put it in the mock:
+ res = MockResponse(response._original_response.msg)
+ jar.extract_cookies(res, req)
+
+
+def get_cookie_header(jar, request):
+ """Produce an appropriate Cookie header string to be sent with `request`, or None."""
+ r = MockRequest(request)
+ jar.add_cookie_header(r)
+ return r.get_new_headers().get('Cookie')
+
+
+def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
+ """Unsets a cookie by name, by default over all domains and paths.
+
+ Wraps CookieJar.clear(), is O(n).
+ """
+ clearables = []
+ for cookie in cookiejar:
+ if cookie.name == name:
+ if domain is None or domain == cookie.domain:
+ if path is None or path == cookie.path:
+ clearables.append((cookie.domain, cookie.path, cookie.name))
+
+ for domain, path, name in clearables:
+ cookiejar.clear(domain, path, name)
+
+
+class CookieConflictError(RuntimeError):
+ """There are two cookies that meet the criteria specified in the cookie jar.
+ Use .get and .set and include domain and path args in order to be more specific."""
+
+
+class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping):
+ """Compatibility class; is a cookielib.CookieJar, but exposes a dict interface.
+
+ This is the CookieJar we create by default for requests and sessions that
+ don't specify one, since some clients may expect response.cookies and
+ session.cookies to support dict operations.
+
+ Don't use the dict interface internally; it's just for compatibility with
+ with external client code. All `requests` code should work out of the box
+ with externally provided instances of CookieJar, e.g., LWPCookieJar and
+ FileCookieJar.
+
+ Caution: dictionary operations that are normally O(1) may be O(n).
+
+ Unlike a regular CookieJar, this class is pickleable.
+ """
+
+ def get(self, name, default=None, domain=None, path=None):
+ """Dict-like get() that also supports optional domain and path args in
+ order to resolve naming collisions from using one cookie jar over
+ multiple domains. Caution: operation is O(n), not O(1)."""
+ try:
+ return self._find_no_duplicates(name, domain, path)
+ except KeyError:
+ return default
+
+ def set(self, name, value, **kwargs):
+ """Dict-like set() that also supports optional domain and path args in
+ order to resolve naming collisions from using one cookie jar over
+ multiple domains."""
+ # support client code that unsets cookies by assignment of a None value:
+ if value is None:
+ remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path'))
+ return
+
+ if isinstance(value, Morsel):
+ c = morsel_to_cookie(value)
+ else:
+ c = create_cookie(name, value, **kwargs)
+ self.set_cookie(c)
+ return c
+
+ def keys(self):
+ """Dict-like keys() that returns a list of names of cookies from the jar.
+ See values() and items()."""
+ keys = []
+ for cookie in iter(self):
+ keys.append(cookie.name)
+ return keys
+
+ def values(self):
+ """Dict-like values() that returns a list of values of cookies from the jar.
+ See keys() and items()."""
+ values = []
+ for cookie in iter(self):
+ values.append(cookie.value)
+ return values
+
+ def items(self):
+ """Dict-like items() that returns a list of name-value tuples from the jar.
+ See keys() and values(). Allows client-code to call "dict(RequestsCookieJar)
+ and get a vanilla python dict of key value pairs."""
+ items = []
+ for cookie in iter(self):
+ items.append((cookie.name, cookie.value))
+ return items
+
+ def list_domains(self):
+ """Utility method to list all the domains in the jar."""
+ domains = []
+ for cookie in iter(self):
+ if cookie.domain not in domains:
+ domains.append(cookie.domain)
+ return domains
+
+ def list_paths(self):
+ """Utility method to list all the paths in the jar."""
+ paths = []
+ for cookie in iter(self):
+ if cookie.path not in paths:
+ paths.append(cookie.path)
+ return paths
+
+ def multiple_domains(self):
+ """Returns True if there are multiple domains in the jar.
+ Returns False otherwise."""
+ domains = []
+ for cookie in iter(self):
+ if cookie.domain is not None and cookie.domain in domains:
+ return True
+ domains.append(cookie.domain)
+ return False # there is only one domain in jar
+
+ def get_dict(self, domain=None, path=None):
+ """Takes as an argument an optional domain and path and returns a plain old
+ Python dict of name-value pairs of cookies that meet the requirements."""
+ dictionary = {}
+ for cookie in iter(self):
+ if (domain == None or cookie.domain == domain) and (path == None
+ or cookie.path == path):
+ dictionary[cookie.name] = cookie.value
+ return dictionary
+
+ def __getitem__(self, name):
+ """Dict-like __getitem__() for compatibility with client code. Throws exception
+ if there are more than one cookie with name. In that case, use the more
+ explicit get() method instead. Caution: operation is O(n), not O(1)."""
+ return self._find_no_duplicates(name)
+
+ def __setitem__(self, name, value):
+ """Dict-like __setitem__ for compatibility with client code. Throws exception
+ if there is already a cookie of that name in the jar. In that case, use the more
+ explicit set() method instead."""
+ self.set(name, value)
+
+ def __delitem__(self, name):
+ """Deletes a cookie given a name. Wraps cookielib.CookieJar's remove_cookie_by_name()."""
+ remove_cookie_by_name(self, name)
+
+ def _find(self, name, domain=None, path=None):
+ """Requests uses this method internally to get cookie values. Takes as args name
+ and optional domain and path. Returns a cookie.value. If there are conflicting cookies,
+ _find arbitrarily chooses one. See _find_no_duplicates if you want an exception thrown
+ if there are conflicting cookies."""
+ for cookie in iter(self):
+ if cookie.name == name:
+ if domain is None or cookie.domain == domain:
+ if path is None or cookie.path == path:
+ return cookie.value
+
+ raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))
+
+ def _find_no_duplicates(self, name, domain=None, path=None):
+ """__get_item__ and get call _find_no_duplicates -- never used in Requests internally.
+ Takes as args name and optional domain and path. Returns a cookie.value.
+ Throws KeyError if cookie is not found and CookieConflictError if there are
+ multiple cookies that match name and optionally domain and path."""
+ toReturn = None
+ for cookie in iter(self):
+ if cookie.name == name:
+ if domain is None or cookie.domain == domain:
+ if path is None or cookie.path == path:
+ if toReturn != None: # if there are multiple cookies that meet passed in criteria
+ raise CookieConflictError('There are multiple cookies with name, %r' % (name))
+ toReturn = cookie.value # we will eventually return this as long as no cookie conflict
+
+ if toReturn:
+ return toReturn
+ raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))
+
+ def __getstate__(self):
+ """Unlike a normal CookieJar, this class is pickleable."""
+ state = self.__dict__.copy()
+ # remove the unpickleable RLock object
+ state.pop('_cookies_lock')
+ return state
+
+ def __setstate__(self, state):
+ """Unlike a normal CookieJar, this class is pickleable."""
+ self.__dict__.update(state)
+ if '_cookies_lock' not in self.__dict__:
+ self._cookies_lock = threading.RLock()
+
+ def copy(self):
+ """This is not implemented. Calling this will throw an exception."""
+ raise NotImplementedError
+
+
+def create_cookie(name, value, **kwargs):
+ """Make a cookie from underspecified parameters.
+
+ By default, the pair of `name` and `value` will be set for the domain ''
+ and sent on every request (this is sometimes called a "supercookie").
+ """
+ result = dict(
+ version=0,
+ name=name,
+ value=value,
+ port=None,
+ domain='',
+ path='/',
+ secure=False,
+ expires=None,
+ discard=True,
+ comment=None,
+ comment_url=None,
+ rest={'HttpOnly': None},
+ rfc2109=False,
+ )
+
+ badargs = set(kwargs) - set(result)
+ if badargs:
+ err = 'create_cookie() got unexpected keyword arguments: %s'
+ raise TypeError(err % list(badargs))
+
+ result.update(kwargs)
+ result['port_specified'] = bool(result['port'])
+ result['domain_specified'] = bool(result['domain'])
+ result['domain_initial_dot'] = result['domain'].startswith('.')
+ result['path_specified'] = bool(result['path'])
+
+ return cookielib.Cookie(**result)
+
+
+def morsel_to_cookie(morsel):
+ """Convert a Morsel object into a Cookie containing the one k/v pair."""
+ c = create_cookie(
+ name=morsel.key,
+ value=morsel.value,
+ version=morsel['version'] or 0,
+ port=None,
+ port_specified=False,
+ domain=morsel['domain'],
+ domain_specified=bool(morsel['domain']),
+ domain_initial_dot=morsel['domain'].startswith('.'),
+ path=morsel['path'],
+ path_specified=bool(morsel['path']),
+ secure=bool(morsel['secure']),
+ expires=morsel['max-age'] or morsel['expires'],
+ discard=False,
+ comment=morsel['comment'],
+ comment_url=bool(morsel['comment']),
+ rest={'HttpOnly': morsel['httponly']},
+ rfc2109=False,
+ )
+ return c
+
+
+def cookiejar_from_dict(cookie_dict, cookiejar=None):
+ """Returns a CookieJar from a key/value dictionary.
+
+ :param cookie_dict: Dict of key/values to insert into CookieJar.
+ """
+ if cookiejar is None:
+ cookiejar = RequestsCookieJar()
+
+ if cookie_dict is not None:
+ for name in cookie_dict:
+ cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
+ return cookiejar