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