Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: third_party/google-endpoints/requests/cookies.py

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

Powered by Google App Engine
This is Rietveld 408576698