Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_vendor/requests/sessions.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:47:31 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:9e54283cc701 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 | |
3 """ | |
4 requests.session | |
5 ~~~~~~~~~~~~~~~~ | |
6 | |
7 This module provides a Session object to manage and persist settings across | |
8 requests (cookies, auth, proxies). | |
9 """ | |
10 import os | |
11 import sys | |
12 import time | |
13 from datetime import timedelta | |
14 | |
15 from .auth import _basic_auth_str | |
16 from .compat import cookielib, is_py3, OrderedDict, urljoin, urlparse, Mapping | |
17 from .cookies import ( | |
18 cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) | |
19 from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT | |
20 from .hooks import default_hooks, dispatch_hook | |
21 from ._internal_utils import to_native_string | |
22 from .utils import to_key_val_list, default_headers, DEFAULT_PORTS | |
23 from .exceptions import ( | |
24 TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) | |
25 | |
26 from .structures import CaseInsensitiveDict | |
27 from .adapters import HTTPAdapter | |
28 | |
29 from .utils import ( | |
30 requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, | |
31 get_auth_from_url, rewind_body | |
32 ) | |
33 | |
34 from .status_codes import codes | |
35 | |
36 # formerly defined here, reexposed here for backward compatibility | |
37 from .models import REDIRECT_STATI | |
38 | |
39 # Preferred clock, based on which one is more accurate on a given system. | |
40 if sys.platform == 'win32': | |
41 try: # Python 3.4+ | |
42 preferred_clock = time.perf_counter | |
43 except AttributeError: # Earlier than Python 3. | |
44 preferred_clock = time.clock | |
45 else: | |
46 preferred_clock = time.time | |
47 | |
48 | |
49 def merge_setting(request_setting, session_setting, dict_class=OrderedDict): | |
50 """Determines appropriate setting for a given request, taking into account | |
51 the explicit setting on that request, and the setting in the session. If a | |
52 setting is a dictionary, they will be merged together using `dict_class` | |
53 """ | |
54 | |
55 if session_setting is None: | |
56 return request_setting | |
57 | |
58 if request_setting is None: | |
59 return session_setting | |
60 | |
61 # Bypass if not a dictionary (e.g. verify) | |
62 if not ( | |
63 isinstance(session_setting, Mapping) and | |
64 isinstance(request_setting, Mapping) | |
65 ): | |
66 return request_setting | |
67 | |
68 merged_setting = dict_class(to_key_val_list(session_setting)) | |
69 merged_setting.update(to_key_val_list(request_setting)) | |
70 | |
71 # Remove keys that are set to None. Extract keys first to avoid altering | |
72 # the dictionary during iteration. | |
73 none_keys = [k for (k, v) in merged_setting.items() if v is None] | |
74 for key in none_keys: | |
75 del merged_setting[key] | |
76 | |
77 return merged_setting | |
78 | |
79 | |
80 def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): | |
81 """Properly merges both requests and session hooks. | |
82 | |
83 This is necessary because when request_hooks == {'response': []}, the | |
84 merge breaks Session hooks entirely. | |
85 """ | |
86 if session_hooks is None or session_hooks.get('response') == []: | |
87 return request_hooks | |
88 | |
89 if request_hooks is None or request_hooks.get('response') == []: | |
90 return session_hooks | |
91 | |
92 return merge_setting(request_hooks, session_hooks, dict_class) | |
93 | |
94 | |
95 class SessionRedirectMixin(object): | |
96 | |
97 def get_redirect_target(self, resp): | |
98 """Receives a Response. Returns a redirect URI or ``None``""" | |
99 # Due to the nature of how requests processes redirects this method will | |
100 # be called at least once upon the original response and at least twice | |
101 # on each subsequent redirect response (if any). | |
102 # If a custom mixin is used to handle this logic, it may be advantageous | |
103 # to cache the redirect location onto the response object as a private | |
104 # attribute. | |
105 if resp.is_redirect: | |
106 location = resp.headers['location'] | |
107 # Currently the underlying http module on py3 decode headers | |
108 # in latin1, but empirical evidence suggests that latin1 is very | |
109 # rarely used with non-ASCII characters in HTTP headers. | |
110 # It is more likely to get UTF8 header rather than latin1. | |
111 # This causes incorrect handling of UTF8 encoded location headers. | |
112 # To solve this, we re-encode the location in latin1. | |
113 if is_py3: | |
114 location = location.encode('latin1') | |
115 return to_native_string(location, 'utf8') | |
116 return None | |
117 | |
118 def should_strip_auth(self, old_url, new_url): | |
119 """Decide whether Authorization header should be removed when redirecting""" | |
120 old_parsed = urlparse(old_url) | |
121 new_parsed = urlparse(new_url) | |
122 if old_parsed.hostname != new_parsed.hostname: | |
123 return True | |
124 # Special case: allow http -> https redirect when using the standard | |
125 # ports. This isn't specified by RFC 7235, but is kept to avoid | |
126 # breaking backwards compatibility with older versions of requests | |
127 # that allowed any redirects on the same host. | |
128 if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) | |
129 and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): | |
130 return False | |
131 | |
132 # Handle default port usage corresponding to scheme. | |
133 changed_port = old_parsed.port != new_parsed.port | |
134 changed_scheme = old_parsed.scheme != new_parsed.scheme | |
135 default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) | |
136 if (not changed_scheme and old_parsed.port in default_port | |
137 and new_parsed.port in default_port): | |
138 return False | |
139 | |
140 # Standard case: root URI must match | |
141 return changed_port or changed_scheme | |
142 | |
143 def resolve_redirects(self, resp, req, stream=False, timeout=None, | |
144 verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): | |
145 """Receives a Response. Returns a generator of Responses or Requests.""" | |
146 | |
147 hist = [] # keep track of history | |
148 | |
149 url = self.get_redirect_target(resp) | |
150 previous_fragment = urlparse(req.url).fragment | |
151 while url: | |
152 prepared_request = req.copy() | |
153 | |
154 # Update history and keep track of redirects. | |
155 # resp.history must ignore the original request in this loop | |
156 hist.append(resp) | |
157 resp.history = hist[1:] | |
158 | |
159 try: | |
160 resp.content # Consume socket so it can be released | |
161 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): | |
162 resp.raw.read(decode_content=False) | |
163 | |
164 if len(resp.history) >= self.max_redirects: | |
165 raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) | |
166 | |
167 # Release the connection back into the pool. | |
168 resp.close() | |
169 | |
170 # Handle redirection without scheme (see: RFC 1808 Section 4) | |
171 if url.startswith('//'): | |
172 parsed_rurl = urlparse(resp.url) | |
173 url = '%s:%s' % (to_native_string(parsed_rurl.scheme), url) | |
174 | |
175 # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) | |
176 parsed = urlparse(url) | |
177 if parsed.fragment == '' and previous_fragment: | |
178 parsed = parsed._replace(fragment=previous_fragment) | |
179 elif parsed.fragment: | |
180 previous_fragment = parsed.fragment | |
181 url = parsed.geturl() | |
182 | |
183 # Facilitate relative 'location' headers, as allowed by RFC 7231. | |
184 # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') | |
185 # Compliant with RFC3986, we percent encode the url. | |
186 if not parsed.netloc: | |
187 url = urljoin(resp.url, requote_uri(url)) | |
188 else: | |
189 url = requote_uri(url) | |
190 | |
191 prepared_request.url = to_native_string(url) | |
192 | |
193 self.rebuild_method(prepared_request, resp) | |
194 | |
195 # https://github.com/requests/requests/issues/1084 | |
196 if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): | |
197 # https://github.com/requests/requests/issues/3490 | |
198 purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') | |
199 for header in purged_headers: | |
200 prepared_request.headers.pop(header, None) | |
201 prepared_request.body = None | |
202 | |
203 headers = prepared_request.headers | |
204 try: | |
205 del headers['Cookie'] | |
206 except KeyError: | |
207 pass | |
208 | |
209 # Extract any cookies sent on the response to the cookiejar | |
210 # in the new request. Because we've mutated our copied prepared | |
211 # request, use the old one that we haven't yet touched. | |
212 extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) | |
213 merge_cookies(prepared_request._cookies, self.cookies) | |
214 prepared_request.prepare_cookies(prepared_request._cookies) | |
215 | |
216 # Rebuild auth and proxy information. | |
217 proxies = self.rebuild_proxies(prepared_request, proxies) | |
218 self.rebuild_auth(prepared_request, resp) | |
219 | |
220 # A failed tell() sets `_body_position` to `object()`. This non-None | |
221 # value ensures `rewindable` will be True, allowing us to raise an | |
222 # UnrewindableBodyError, instead of hanging the connection. | |
223 rewindable = ( | |
224 prepared_request._body_position is not None and | |
225 ('Content-Length' in headers or 'Transfer-Encoding' in headers) | |
226 ) | |
227 | |
228 # Attempt to rewind consumed file-like object. | |
229 if rewindable: | |
230 rewind_body(prepared_request) | |
231 | |
232 # Override the original request. | |
233 req = prepared_request | |
234 | |
235 if yield_requests: | |
236 yield req | |
237 else: | |
238 | |
239 resp = self.send( | |
240 req, | |
241 stream=stream, | |
242 timeout=timeout, | |
243 verify=verify, | |
244 cert=cert, | |
245 proxies=proxies, | |
246 allow_redirects=False, | |
247 **adapter_kwargs | |
248 ) | |
249 | |
250 extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) | |
251 | |
252 # extract redirect url, if any, for the next loop | |
253 url = self.get_redirect_target(resp) | |
254 yield resp | |
255 | |
256 def rebuild_auth(self, prepared_request, response): | |
257 """When being redirected we may want to strip authentication from the | |
258 request to avoid leaking credentials. This method intelligently removes | |
259 and reapplies authentication where possible to avoid credential loss. | |
260 """ | |
261 headers = prepared_request.headers | |
262 url = prepared_request.url | |
263 | |
264 if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): | |
265 # If we get redirected to a new host, we should strip out any | |
266 # authentication headers. | |
267 del headers['Authorization'] | |
268 | |
269 # .netrc might have more auth for us on our new host. | |
270 new_auth = get_netrc_auth(url) if self.trust_env else None | |
271 if new_auth is not None: | |
272 prepared_request.prepare_auth(new_auth) | |
273 | |
274 return | |
275 | |
276 def rebuild_proxies(self, prepared_request, proxies): | |
277 """This method re-evaluates the proxy configuration by considering the | |
278 environment variables. If we are redirected to a URL covered by | |
279 NO_PROXY, we strip the proxy configuration. Otherwise, we set missing | |
280 proxy keys for this URL (in case they were stripped by a previous | |
281 redirect). | |
282 | |
283 This method also replaces the Proxy-Authorization header where | |
284 necessary. | |
285 | |
286 :rtype: dict | |
287 """ | |
288 proxies = proxies if proxies is not None else {} | |
289 headers = prepared_request.headers | |
290 url = prepared_request.url | |
291 scheme = urlparse(url).scheme | |
292 new_proxies = proxies.copy() | |
293 no_proxy = proxies.get('no_proxy') | |
294 | |
295 bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) | |
296 if self.trust_env and not bypass_proxy: | |
297 environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) | |
298 | |
299 proxy = environ_proxies.get(scheme, environ_proxies.get('all')) | |
300 | |
301 if proxy: | |
302 new_proxies.setdefault(scheme, proxy) | |
303 | |
304 if 'Proxy-Authorization' in headers: | |
305 del headers['Proxy-Authorization'] | |
306 | |
307 try: | |
308 username, password = get_auth_from_url(new_proxies[scheme]) | |
309 except KeyError: | |
310 username, password = None, None | |
311 | |
312 if username and password: | |
313 headers['Proxy-Authorization'] = _basic_auth_str(username, password) | |
314 | |
315 return new_proxies | |
316 | |
317 def rebuild_method(self, prepared_request, response): | |
318 """When being redirected we may want to change the method of the request | |
319 based on certain specs or browser behavior. | |
320 """ | |
321 method = prepared_request.method | |
322 | |
323 # https://tools.ietf.org/html/rfc7231#section-6.4.4 | |
324 if response.status_code == codes.see_other and method != 'HEAD': | |
325 method = 'GET' | |
326 | |
327 # Do what the browsers do, despite standards... | |
328 # First, turn 302s into GETs. | |
329 if response.status_code == codes.found and method != 'HEAD': | |
330 method = 'GET' | |
331 | |
332 # Second, if a POST is responded to with a 301, turn it into a GET. | |
333 # This bizarre behaviour is explained in Issue 1704. | |
334 if response.status_code == codes.moved and method == 'POST': | |
335 method = 'GET' | |
336 | |
337 prepared_request.method = method | |
338 | |
339 | |
340 class Session(SessionRedirectMixin): | |
341 """A Requests session. | |
342 | |
343 Provides cookie persistence, connection-pooling, and configuration. | |
344 | |
345 Basic Usage:: | |
346 | |
347 >>> import requests | |
348 >>> s = requests.Session() | |
349 >>> s.get('https://httpbin.org/get') | |
350 <Response [200]> | |
351 | |
352 Or as a context manager:: | |
353 | |
354 >>> with requests.Session() as s: | |
355 >>> s.get('https://httpbin.org/get') | |
356 <Response [200]> | |
357 """ | |
358 | |
359 __attrs__ = [ | |
360 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', | |
361 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', | |
362 'max_redirects', | |
363 ] | |
364 | |
365 def __init__(self): | |
366 | |
367 #: A case-insensitive dictionary of headers to be sent on each | |
368 #: :class:`Request <Request>` sent from this | |
369 #: :class:`Session <Session>`. | |
370 self.headers = default_headers() | |
371 | |
372 #: Default Authentication tuple or object to attach to | |
373 #: :class:`Request <Request>`. | |
374 self.auth = None | |
375 | |
376 #: Dictionary mapping protocol or protocol and host to the URL of the proxy | |
377 #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to | |
378 #: be used on each :class:`Request <Request>`. | |
379 self.proxies = {} | |
380 | |
381 #: Event-handling hooks. | |
382 self.hooks = default_hooks() | |
383 | |
384 #: Dictionary of querystring data to attach to each | |
385 #: :class:`Request <Request>`. The dictionary values may be lists for | |
386 #: representing multivalued query parameters. | |
387 self.params = {} | |
388 | |
389 #: Stream response content default. | |
390 self.stream = False | |
391 | |
392 #: SSL Verification default. | |
393 self.verify = True | |
394 | |
395 #: SSL client certificate default, if String, path to ssl client | |
396 #: cert file (.pem). If Tuple, ('cert', 'key') pair. | |
397 self.cert = None | |
398 | |
399 #: Maximum number of redirects allowed. If the request exceeds this | |
400 #: limit, a :class:`TooManyRedirects` exception is raised. | |
401 #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is | |
402 #: 30. | |
403 self.max_redirects = DEFAULT_REDIRECT_LIMIT | |
404 | |
405 #: Trust environment settings for proxy configuration, default | |
406 #: authentication and similar. | |
407 self.trust_env = True | |
408 | |
409 #: A CookieJar containing all currently outstanding cookies set on this | |
410 #: session. By default it is a | |
411 #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but | |
412 #: may be any other ``cookielib.CookieJar`` compatible object. | |
413 self.cookies = cookiejar_from_dict({}) | |
414 | |
415 # Default connection adapters. | |
416 self.adapters = OrderedDict() | |
417 self.mount('https://', HTTPAdapter()) | |
418 self.mount('http://', HTTPAdapter()) | |
419 | |
420 def __enter__(self): | |
421 return self | |
422 | |
423 def __exit__(self, *args): | |
424 self.close() | |
425 | |
426 def prepare_request(self, request): | |
427 """Constructs a :class:`PreparedRequest <PreparedRequest>` for | |
428 transmission and returns it. The :class:`PreparedRequest` has settings | |
429 merged from the :class:`Request <Request>` instance and those of the | |
430 :class:`Session`. | |
431 | |
432 :param request: :class:`Request` instance to prepare with this | |
433 session's settings. | |
434 :rtype: requests.PreparedRequest | |
435 """ | |
436 cookies = request.cookies or {} | |
437 | |
438 # Bootstrap CookieJar. | |
439 if not isinstance(cookies, cookielib.CookieJar): | |
440 cookies = cookiejar_from_dict(cookies) | |
441 | |
442 # Merge with session cookies | |
443 merged_cookies = merge_cookies( | |
444 merge_cookies(RequestsCookieJar(), self.cookies), cookies) | |
445 | |
446 # Set environment's basic authentication if not explicitly set. | |
447 auth = request.auth | |
448 if self.trust_env and not auth and not self.auth: | |
449 auth = get_netrc_auth(request.url) | |
450 | |
451 p = PreparedRequest() | |
452 p.prepare( | |
453 method=request.method.upper(), | |
454 url=request.url, | |
455 files=request.files, | |
456 data=request.data, | |
457 json=request.json, | |
458 headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), | |
459 params=merge_setting(request.params, self.params), | |
460 auth=merge_setting(auth, self.auth), | |
461 cookies=merged_cookies, | |
462 hooks=merge_hooks(request.hooks, self.hooks), | |
463 ) | |
464 return p | |
465 | |
466 def request(self, method, url, | |
467 params=None, data=None, headers=None, cookies=None, files=None, | |
468 auth=None, timeout=None, allow_redirects=True, proxies=None, | |
469 hooks=None, stream=None, verify=None, cert=None, json=None): | |
470 """Constructs a :class:`Request <Request>`, prepares it and sends it. | |
471 Returns :class:`Response <Response>` object. | |
472 | |
473 :param method: method for the new :class:`Request` object. | |
474 :param url: URL for the new :class:`Request` object. | |
475 :param params: (optional) Dictionary or bytes to be sent in the query | |
476 string for the :class:`Request`. | |
477 :param data: (optional) Dictionary, list of tuples, bytes, or file-like | |
478 object to send in the body of the :class:`Request`. | |
479 :param json: (optional) json to send in the body of the | |
480 :class:`Request`. | |
481 :param headers: (optional) Dictionary of HTTP Headers to send with the | |
482 :class:`Request`. | |
483 :param cookies: (optional) Dict or CookieJar object to send with the | |
484 :class:`Request`. | |
485 :param files: (optional) Dictionary of ``'filename': file-like-objects`` | |
486 for multipart encoding upload. | |
487 :param auth: (optional) Auth tuple or callable to enable | |
488 Basic/Digest/Custom HTTP Auth. | |
489 :param timeout: (optional) How long to wait for the server to send | |
490 data before giving up, as a float, or a :ref:`(connect timeout, | |
491 read timeout) <timeouts>` tuple. | |
492 :type timeout: float or tuple | |
493 :param allow_redirects: (optional) Set to True by default. | |
494 :type allow_redirects: bool | |
495 :param proxies: (optional) Dictionary mapping protocol or protocol and | |
496 hostname to the URL of the proxy. | |
497 :param stream: (optional) whether to immediately download the response | |
498 content. Defaults to ``False``. | |
499 :param verify: (optional) Either a boolean, in which case it controls whether we verify | |
500 the server's TLS certificate, or a string, in which case it must be a path | |
501 to a CA bundle to use. Defaults to ``True``. | |
502 :param cert: (optional) if String, path to ssl client cert file (.pem). | |
503 If Tuple, ('cert', 'key') pair. | |
504 :rtype: requests.Response | |
505 """ | |
506 # Create the Request. | |
507 req = Request( | |
508 method=method.upper(), | |
509 url=url, | |
510 headers=headers, | |
511 files=files, | |
512 data=data or {}, | |
513 json=json, | |
514 params=params or {}, | |
515 auth=auth, | |
516 cookies=cookies, | |
517 hooks=hooks, | |
518 ) | |
519 prep = self.prepare_request(req) | |
520 | |
521 proxies = proxies or {} | |
522 | |
523 settings = self.merge_environment_settings( | |
524 prep.url, proxies, stream, verify, cert | |
525 ) | |
526 | |
527 # Send the request. | |
528 send_kwargs = { | |
529 'timeout': timeout, | |
530 'allow_redirects': allow_redirects, | |
531 } | |
532 send_kwargs.update(settings) | |
533 resp = self.send(prep, **send_kwargs) | |
534 | |
535 return resp | |
536 | |
537 def get(self, url, **kwargs): | |
538 r"""Sends a GET request. Returns :class:`Response` object. | |
539 | |
540 :param url: URL for the new :class:`Request` object. | |
541 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
542 :rtype: requests.Response | |
543 """ | |
544 | |
545 kwargs.setdefault('allow_redirects', True) | |
546 return self.request('GET', url, **kwargs) | |
547 | |
548 def options(self, url, **kwargs): | |
549 r"""Sends a OPTIONS request. Returns :class:`Response` object. | |
550 | |
551 :param url: URL for the new :class:`Request` object. | |
552 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
553 :rtype: requests.Response | |
554 """ | |
555 | |
556 kwargs.setdefault('allow_redirects', True) | |
557 return self.request('OPTIONS', url, **kwargs) | |
558 | |
559 def head(self, url, **kwargs): | |
560 r"""Sends a HEAD request. Returns :class:`Response` object. | |
561 | |
562 :param url: URL for the new :class:`Request` object. | |
563 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
564 :rtype: requests.Response | |
565 """ | |
566 | |
567 kwargs.setdefault('allow_redirects', False) | |
568 return self.request('HEAD', url, **kwargs) | |
569 | |
570 def post(self, url, data=None, json=None, **kwargs): | |
571 r"""Sends a POST request. Returns :class:`Response` object. | |
572 | |
573 :param url: URL for the new :class:`Request` object. | |
574 :param data: (optional) Dictionary, list of tuples, bytes, or file-like | |
575 object to send in the body of the :class:`Request`. | |
576 :param json: (optional) json to send in the body of the :class:`Request`. | |
577 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
578 :rtype: requests.Response | |
579 """ | |
580 | |
581 return self.request('POST', url, data=data, json=json, **kwargs) | |
582 | |
583 def put(self, url, data=None, **kwargs): | |
584 r"""Sends a PUT request. Returns :class:`Response` object. | |
585 | |
586 :param url: URL for the new :class:`Request` object. | |
587 :param data: (optional) Dictionary, list of tuples, bytes, or file-like | |
588 object to send in the body of the :class:`Request`. | |
589 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
590 :rtype: requests.Response | |
591 """ | |
592 | |
593 return self.request('PUT', url, data=data, **kwargs) | |
594 | |
595 def patch(self, url, data=None, **kwargs): | |
596 r"""Sends a PATCH request. Returns :class:`Response` object. | |
597 | |
598 :param url: URL for the new :class:`Request` object. | |
599 :param data: (optional) Dictionary, list of tuples, bytes, or file-like | |
600 object to send in the body of the :class:`Request`. | |
601 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
602 :rtype: requests.Response | |
603 """ | |
604 | |
605 return self.request('PATCH', url, data=data, **kwargs) | |
606 | |
607 def delete(self, url, **kwargs): | |
608 r"""Sends a DELETE request. Returns :class:`Response` object. | |
609 | |
610 :param url: URL for the new :class:`Request` object. | |
611 :param \*\*kwargs: Optional arguments that ``request`` takes. | |
612 :rtype: requests.Response | |
613 """ | |
614 | |
615 return self.request('DELETE', url, **kwargs) | |
616 | |
617 def send(self, request, **kwargs): | |
618 """Send a given PreparedRequest. | |
619 | |
620 :rtype: requests.Response | |
621 """ | |
622 # Set defaults that the hooks can utilize to ensure they always have | |
623 # the correct parameters to reproduce the previous request. | |
624 kwargs.setdefault('stream', self.stream) | |
625 kwargs.setdefault('verify', self.verify) | |
626 kwargs.setdefault('cert', self.cert) | |
627 kwargs.setdefault('proxies', self.proxies) | |
628 | |
629 # It's possible that users might accidentally send a Request object. | |
630 # Guard against that specific failure case. | |
631 if isinstance(request, Request): | |
632 raise ValueError('You can only send PreparedRequests.') | |
633 | |
634 # Set up variables needed for resolve_redirects and dispatching of hooks | |
635 allow_redirects = kwargs.pop('allow_redirects', True) | |
636 stream = kwargs.get('stream') | |
637 hooks = request.hooks | |
638 | |
639 # Get the appropriate adapter to use | |
640 adapter = self.get_adapter(url=request.url) | |
641 | |
642 # Start time (approximately) of the request | |
643 start = preferred_clock() | |
644 | |
645 # Send the request | |
646 r = adapter.send(request, **kwargs) | |
647 | |
648 # Total elapsed time of the request (approximately) | |
649 elapsed = preferred_clock() - start | |
650 r.elapsed = timedelta(seconds=elapsed) | |
651 | |
652 # Response manipulation hooks | |
653 r = dispatch_hook('response', hooks, r, **kwargs) | |
654 | |
655 # Persist cookies | |
656 if r.history: | |
657 | |
658 # If the hooks create history then we want those cookies too | |
659 for resp in r.history: | |
660 extract_cookies_to_jar(self.cookies, resp.request, resp.raw) | |
661 | |
662 extract_cookies_to_jar(self.cookies, request, r.raw) | |
663 | |
664 # Redirect resolving generator. | |
665 gen = self.resolve_redirects(r, request, **kwargs) | |
666 | |
667 # Resolve redirects if allowed. | |
668 history = [resp for resp in gen] if allow_redirects else [] | |
669 | |
670 # Shuffle things around if there's history. | |
671 if history: | |
672 # Insert the first (original) request at the start | |
673 history.insert(0, r) | |
674 # Get the last request made | |
675 r = history.pop() | |
676 r.history = history | |
677 | |
678 # If redirects aren't being followed, store the response on the Request for Response.next(). | |
679 if not allow_redirects: | |
680 try: | |
681 r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) | |
682 except StopIteration: | |
683 pass | |
684 | |
685 if not stream: | |
686 r.content | |
687 | |
688 return r | |
689 | |
690 def merge_environment_settings(self, url, proxies, stream, verify, cert): | |
691 """ | |
692 Check the environment and merge it with some settings. | |
693 | |
694 :rtype: dict | |
695 """ | |
696 # Gather clues from the surrounding environment. | |
697 if self.trust_env: | |
698 # Set environment's proxies. | |
699 no_proxy = proxies.get('no_proxy') if proxies is not None else None | |
700 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) | |
701 for (k, v) in env_proxies.items(): | |
702 proxies.setdefault(k, v) | |
703 | |
704 # Look for requests environment configuration and be compatible | |
705 # with cURL. | |
706 if verify is True or verify is None: | |
707 verify = (os.environ.get('REQUESTS_CA_BUNDLE') or | |
708 os.environ.get('CURL_CA_BUNDLE')) | |
709 | |
710 # Merge all the kwargs. | |
711 proxies = merge_setting(proxies, self.proxies) | |
712 stream = merge_setting(stream, self.stream) | |
713 verify = merge_setting(verify, self.verify) | |
714 cert = merge_setting(cert, self.cert) | |
715 | |
716 return {'verify': verify, 'proxies': proxies, 'stream': stream, | |
717 'cert': cert} | |
718 | |
719 def get_adapter(self, url): | |
720 """ | |
721 Returns the appropriate connection adapter for the given URL. | |
722 | |
723 :rtype: requests.adapters.BaseAdapter | |
724 """ | |
725 for (prefix, adapter) in self.adapters.items(): | |
726 | |
727 if url.lower().startswith(prefix.lower()): | |
728 return adapter | |
729 | |
730 # Nothing matches :-/ | |
731 raise InvalidSchema("No connection adapters were found for '%s'" % url) | |
732 | |
733 def close(self): | |
734 """Closes all adapters and as such the session""" | |
735 for v in self.adapters.values(): | |
736 v.close() | |
737 | |
738 def mount(self, prefix, adapter): | |
739 """Registers a connection adapter to a prefix. | |
740 | |
741 Adapters are sorted in descending order by prefix length. | |
742 """ | |
743 self.adapters[prefix] = adapter | |
744 keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] | |
745 | |
746 for key in keys_to_move: | |
747 self.adapters[key] = self.adapters.pop(key) | |
748 | |
749 def __getstate__(self): | |
750 state = {attr: getattr(self, attr, None) for attr in self.__attrs__} | |
751 return state | |
752 | |
753 def __setstate__(self, state): | |
754 for attr, value in state.items(): | |
755 setattr(self, attr, value) | |
756 | |
757 | |
758 def session(): | |
759 """ | |
760 Returns a :class:`Session` for context-management. | |
761 | |
762 .. deprecated:: 1.0.0 | |
763 | |
764 This method has been deprecated since version 1.0.0 and is only kept for | |
765 backwards compatibility. New code should use :class:`~requests.sessions.Session` | |
766 to create a session. This may be removed at a future date. | |
767 | |
768 :rtype: Session | |
769 """ | |
770 return Session() |