Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2 

3""" 

4requests.sessions 

5~~~~~~~~~~~~~~~~~ 

6 

7This module provides a Session object to manage and persist settings across 

8requests (cookies, auth, proxies). 

9""" 

10import os 

11import sys 

12import time 

13from datetime import timedelta 

14from collections import OrderedDict 

15 

16from .auth import _basic_auth_str 

17from .compat import cookielib, is_py3, urljoin, urlparse, Mapping 

18from .cookies import ( 

19 cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) 

20from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT 

21from .hooks import default_hooks, dispatch_hook 

22from ._internal_utils import to_native_string 

23from .utils import to_key_val_list, default_headers, DEFAULT_PORTS 

24from .exceptions import ( 

25 TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) 

26 

27from .structures import CaseInsensitiveDict 

28from .adapters import HTTPAdapter 

29 

30from .utils import ( 

31 requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, 

32 get_auth_from_url, rewind_body 

33) 

34 

35from .status_codes import codes 

36 

37# formerly defined here, reexposed here for backward compatibility 

38from .models import REDIRECT_STATI 

39 

40# Preferred clock, based on which one is more accurate on a given system. 

41if sys.platform == 'win32': 

42 try: # Python 3.4+ 

43 preferred_clock = time.perf_counter 

44 except AttributeError: # Earlier than Python 3. 

45 preferred_clock = time.clock 

46else: 

47 preferred_clock = time.time 

48 

49 

50def merge_setting(request_setting, session_setting, dict_class=OrderedDict): 

51 """Determines appropriate setting for a given request, taking into account 

52 the explicit setting on that request, and the setting in the session. If a 

53 setting is a dictionary, they will be merged together using `dict_class` 

54 """ 

55 

56 if session_setting is None: 

57 return request_setting 

58 

59 if request_setting is None: 

60 return session_setting 

61 

62 # Bypass if not a dictionary (e.g. verify) 

63 if not ( 

64 isinstance(session_setting, Mapping) and 

65 isinstance(request_setting, Mapping) 

66 ): 

67 return request_setting 

68 

69 merged_setting = dict_class(to_key_val_list(session_setting)) 

70 merged_setting.update(to_key_val_list(request_setting)) 

71 

72 # Remove keys that are set to None. Extract keys first to avoid altering 

73 # the dictionary during iteration. 

74 none_keys = [k for (k, v) in merged_setting.items() if v is None] 

75 for key in none_keys: 

76 del merged_setting[key] 

77 

78 return merged_setting 

79 

80 

81def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): 

82 """Properly merges both requests and session hooks. 

83 

84 This is necessary because when request_hooks == {'response': []}, the 

85 merge breaks Session hooks entirely. 

86 """ 

87 if session_hooks is None or session_hooks.get('response') == []: 

88 return request_hooks 

89 

90 if request_hooks is None or request_hooks.get('response') == []: 

91 return session_hooks 

92 

93 return merge_setting(request_hooks, session_hooks, dict_class) 

94 

95 

96class SessionRedirectMixin(object): 

97 

98 def get_redirect_target(self, resp): 

99 """Receives a Response. Returns a redirect URI or ``None``""" 

100 # Due to the nature of how requests processes redirects this method will 

101 # be called at least once upon the original response and at least twice 

102 # on each subsequent redirect response (if any). 

103 # If a custom mixin is used to handle this logic, it may be advantageous 

104 # to cache the redirect location onto the response object as a private 

105 # attribute. 

106 if resp.is_redirect: 

107 location = resp.headers['location'] 

108 # Currently the underlying http module on py3 decode headers 

109 # in latin1, but empirical evidence suggests that latin1 is very 

110 # rarely used with non-ASCII characters in HTTP headers. 

111 # It is more likely to get UTF8 header rather than latin1. 

112 # This causes incorrect handling of UTF8 encoded location headers. 

113 # To solve this, we re-encode the location in latin1. 

114 if is_py3: 

115 location = location.encode('latin1') 

116 return to_native_string(location, 'utf8') 

117 return None 

118 

119 def should_strip_auth(self, old_url, new_url): 

120 """Decide whether Authorization header should be removed when redirecting""" 

121 old_parsed = urlparse(old_url) 

122 new_parsed = urlparse(new_url) 

123 if old_parsed.hostname != new_parsed.hostname: 

124 return True 

125 # Special case: allow http -> https redirect when using the standard 

126 # ports. This isn't specified by RFC 7235, but is kept to avoid 

127 # breaking backwards compatibility with older versions of requests 

128 # that allowed any redirects on the same host. 

129 if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) 

130 and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): 

131 return False 

132 

133 # Handle default port usage corresponding to scheme. 

134 changed_port = old_parsed.port != new_parsed.port 

135 changed_scheme = old_parsed.scheme != new_parsed.scheme 

136 default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) 

137 if (not changed_scheme and old_parsed.port in default_port 

138 and new_parsed.port in default_port): 

139 return False 

140 

141 # Standard case: root URI must match 

142 return changed_port or changed_scheme 

143 

144 def resolve_redirects(self, resp, req, stream=False, timeout=None, 

145 verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): 

146 """Receives a Response. Returns a generator of Responses or Requests.""" 

147 

148 hist = [] # keep track of history 

149 

150 url = self.get_redirect_target(resp) 

151 previous_fragment = urlparse(req.url).fragment 

152 while url: 

153 prepared_request = req.copy() 

154 

155 # Update history and keep track of redirects. 

156 # resp.history must ignore the original request in this loop 

157 hist.append(resp) 

158 resp.history = hist[1:] 

159 

160 try: 

161 resp.content # Consume socket so it can be released 

162 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): 

163 resp.raw.read(decode_content=False) 

164 

165 if len(resp.history) >= self.max_redirects: 

166 raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp) 

167 

168 # Release the connection back into the pool. 

169 resp.close() 

170 

171 # Handle redirection without scheme (see: RFC 1808 Section 4) 

172 if url.startswith('//'): 

173 parsed_rurl = urlparse(resp.url) 

174 url = ':'.join([to_native_string(parsed_rurl.scheme), url]) 

175 

176 # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) 

177 parsed = urlparse(url) 

178 if parsed.fragment == '' and previous_fragment: 

179 parsed = parsed._replace(fragment=previous_fragment) 

180 elif parsed.fragment: 

181 previous_fragment = parsed.fragment 

182 url = parsed.geturl() 

183 

184 # Facilitate relative 'location' headers, as allowed by RFC 7231. 

185 # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') 

186 # Compliant with RFC3986, we percent encode the url. 

187 if not parsed.netloc: 

188 url = urljoin(resp.url, requote_uri(url)) 

189 else: 

190 url = requote_uri(url) 

191 

192 prepared_request.url = to_native_string(url) 

193 

194 self.rebuild_method(prepared_request, resp) 

195 

196 # https://github.com/psf/requests/issues/1084 

197 if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): 

198 # https://github.com/psf/requests/issues/3490 

199 purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') 

200 for header in purged_headers: 

201 prepared_request.headers.pop(header, None) 

202 prepared_request.body = None 

203 

204 headers = prepared_request.headers 

205 headers.pop('Cookie', None) 

206 

207 # Extract any cookies sent on the response to the cookiejar 

208 # in the new request. Because we've mutated our copied prepared 

209 # request, use the old one that we haven't yet touched. 

210 extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) 

211 merge_cookies(prepared_request._cookies, self.cookies) 

212 prepared_request.prepare_cookies(prepared_request._cookies) 

213 

214 # Rebuild auth and proxy information. 

215 proxies = self.rebuild_proxies(prepared_request, proxies) 

216 self.rebuild_auth(prepared_request, resp) 

217 

218 # A failed tell() sets `_body_position` to `object()`. This non-None 

219 # value ensures `rewindable` will be True, allowing us to raise an 

220 # UnrewindableBodyError, instead of hanging the connection. 

221 rewindable = ( 

222 prepared_request._body_position is not None and 

223 ('Content-Length' in headers or 'Transfer-Encoding' in headers) 

224 ) 

225 

226 # Attempt to rewind consumed file-like object. 

227 if rewindable: 

228 rewind_body(prepared_request) 

229 

230 # Override the original request. 

231 req = prepared_request 

232 

233 if yield_requests: 

234 yield req 

235 else: 

236 

237 resp = self.send( 

238 req, 

239 stream=stream, 

240 timeout=timeout, 

241 verify=verify, 

242 cert=cert, 

243 proxies=proxies, 

244 allow_redirects=False, 

245 **adapter_kwargs 

246 ) 

247 

248 extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) 

249 

250 # extract redirect url, if any, for the next loop 

251 url = self.get_redirect_target(resp) 

252 yield resp 

253 

254 def rebuild_auth(self, prepared_request, response): 

255 """When being redirected we may want to strip authentication from the 

256 request to avoid leaking credentials. This method intelligently removes 

257 and reapplies authentication where possible to avoid credential loss. 

258 """ 

259 headers = prepared_request.headers 

260 url = prepared_request.url 

261 

262 if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): 

263 # If we get redirected to a new host, we should strip out any 

264 # authentication headers. 

265 del headers['Authorization'] 

266 

267 # .netrc might have more auth for us on our new host. 

268 new_auth = get_netrc_auth(url) if self.trust_env else None 

269 if new_auth is not None: 

270 prepared_request.prepare_auth(new_auth) 

271 

272 

273 def rebuild_proxies(self, prepared_request, proxies): 

274 """This method re-evaluates the proxy configuration by considering the 

275 environment variables. If we are redirected to a URL covered by 

276 NO_PROXY, we strip the proxy configuration. Otherwise, we set missing 

277 proxy keys for this URL (in case they were stripped by a previous 

278 redirect). 

279 

280 This method also replaces the Proxy-Authorization header where 

281 necessary. 

282 

283 :rtype: dict 

284 """ 

285 proxies = proxies if proxies is not None else {} 

286 headers = prepared_request.headers 

287 url = prepared_request.url 

288 scheme = urlparse(url).scheme 

289 new_proxies = proxies.copy() 

290 no_proxy = proxies.get('no_proxy') 

291 

292 bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) 

293 if self.trust_env and not bypass_proxy: 

294 environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) 

295 

296 proxy = environ_proxies.get(scheme, environ_proxies.get('all')) 

297 

298 if proxy: 

299 new_proxies.setdefault(scheme, proxy) 

300 

301 if 'Proxy-Authorization' in headers: 

302 del headers['Proxy-Authorization'] 

303 

304 try: 

305 username, password = get_auth_from_url(new_proxies[scheme]) 

306 except KeyError: 

307 username, password = None, None 

308 

309 if username and password: 

310 headers['Proxy-Authorization'] = _basic_auth_str(username, password) 

311 

312 return new_proxies 

313 

314 def rebuild_method(self, prepared_request, response): 

315 """When being redirected we may want to change the method of the request 

316 based on certain specs or browser behavior. 

317 """ 

318 method = prepared_request.method 

319 

320 # https://tools.ietf.org/html/rfc7231#section-6.4.4 

321 if response.status_code == codes.see_other and method != 'HEAD': 

322 method = 'GET' 

323 

324 # Do what the browsers do, despite standards... 

325 # First, turn 302s into GETs. 

326 if response.status_code == codes.found and method != 'HEAD': 

327 method = 'GET' 

328 

329 # Second, if a POST is responded to with a 301, turn it into a GET. 

330 # This bizarre behaviour is explained in Issue 1704. 

331 if response.status_code == codes.moved and method == 'POST': 

332 method = 'GET' 

333 

334 prepared_request.method = method 

335 

336 

337class Session(SessionRedirectMixin): 

338 """A Requests session. 

339 

340 Provides cookie persistence, connection-pooling, and configuration. 

341 

342 Basic Usage:: 

343 

344 >>> import requests 

345 >>> s = requests.Session() 

346 >>> s.get('https://httpbin.org/get') 

347 <Response [200]> 

348 

349 Or as a context manager:: 

350 

351 >>> with requests.Session() as s: 

352 ... s.get('https://httpbin.org/get') 

353 <Response [200]> 

354 """ 

355 

356 __attrs__ = [ 

357 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', 

358 'cert', 'adapters', 'stream', 'trust_env', 

359 'max_redirects', 

360 ] 

361 

362 def __init__(self): 

363 

364 #: A case-insensitive dictionary of headers to be sent on each 

365 #: :class:`Request <Request>` sent from this 

366 #: :class:`Session <Session>`. 

367 self.headers = default_headers() 

368 

369 #: Default Authentication tuple or object to attach to 

370 #: :class:`Request <Request>`. 

371 self.auth = None 

372 

373 #: Dictionary mapping protocol or protocol and host to the URL of the proxy 

374 #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to 

375 #: be used on each :class:`Request <Request>`. 

376 self.proxies = {} 

377 

378 #: Event-handling hooks. 

379 self.hooks = default_hooks() 

380 

381 #: Dictionary of querystring data to attach to each 

382 #: :class:`Request <Request>`. The dictionary values may be lists for 

383 #: representing multivalued query parameters. 

384 self.params = {} 

385 

386 #: Stream response content default. 

387 self.stream = False 

388 

389 #: SSL Verification default. 

390 #: Defaults to `True`, requiring requests to verify the TLS certificate at the 

391 #: remote end. 

392 #: If verify is set to `False`, requests will accept any TLS certificate 

393 #: presented by the server, and will ignore hostname mismatches and/or 

394 #: expired certificates, which will make your application vulnerable to 

395 #: man-in-the-middle (MitM) attacks. 

396 #: Only set this to `False` for testing. 

397 self.verify = True 

398 

399 #: SSL client certificate default, if String, path to ssl client 

400 #: cert file (.pem). If Tuple, ('cert', 'key') pair. 

401 self.cert = None 

402 

403 #: Maximum number of redirects allowed. If the request exceeds this 

404 #: limit, a :class:`TooManyRedirects` exception is raised. 

405 #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is 

406 #: 30. 

407 self.max_redirects = DEFAULT_REDIRECT_LIMIT 

408 

409 #: Trust environment settings for proxy configuration, default 

410 #: authentication and similar. 

411 self.trust_env = True 

412 

413 #: A CookieJar containing all currently outstanding cookies set on this 

414 #: session. By default it is a 

415 #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but 

416 #: may be any other ``cookielib.CookieJar`` compatible object. 

417 self.cookies = cookiejar_from_dict({}) 

418 

419 # Default connection adapters. 

420 self.adapters = OrderedDict() 

421 self.mount('https://', HTTPAdapter()) 

422 self.mount('http://', HTTPAdapter()) 

423 

424 def __enter__(self): 

425 return self 

426 

427 def __exit__(self, *args): 

428 self.close() 

429 

430 def prepare_request(self, request): 

431 """Constructs a :class:`PreparedRequest <PreparedRequest>` for 

432 transmission and returns it. The :class:`PreparedRequest` has settings 

433 merged from the :class:`Request <Request>` instance and those of the 

434 :class:`Session`. 

435 

436 :param request: :class:`Request` instance to prepare with this 

437 session's settings. 

438 :rtype: requests.PreparedRequest 

439 """ 

440 cookies = request.cookies or {} 

441 

442 # Bootstrap CookieJar. 

443 if not isinstance(cookies, cookielib.CookieJar): 

444 cookies = cookiejar_from_dict(cookies) 

445 

446 # Merge with session cookies 

447 merged_cookies = merge_cookies( 

448 merge_cookies(RequestsCookieJar(), self.cookies), cookies) 

449 

450 # Set environment's basic authentication if not explicitly set. 

451 auth = request.auth 

452 if self.trust_env and not auth and not self.auth: 

453 auth = get_netrc_auth(request.url) 

454 

455 p = PreparedRequest() 

456 p.prepare( 

457 method=request.method.upper(), 

458 url=request.url, 

459 files=request.files, 

460 data=request.data, 

461 json=request.json, 

462 headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), 

463 params=merge_setting(request.params, self.params), 

464 auth=merge_setting(auth, self.auth), 

465 cookies=merged_cookies, 

466 hooks=merge_hooks(request.hooks, self.hooks), 

467 ) 

468 return p 

469 

470 def request(self, method, url, 

471 params=None, data=None, headers=None, cookies=None, files=None, 

472 auth=None, timeout=None, allow_redirects=True, proxies=None, 

473 hooks=None, stream=None, verify=None, cert=None, json=None): 

474 """Constructs a :class:`Request <Request>`, prepares it and sends it. 

475 Returns :class:`Response <Response>` object. 

476 

477 :param method: method for the new :class:`Request` object. 

478 :param url: URL for the new :class:`Request` object. 

479 :param params: (optional) Dictionary or bytes to be sent in the query 

480 string for the :class:`Request`. 

481 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

482 object to send in the body of the :class:`Request`. 

483 :param json: (optional) json to send in the body of the 

484 :class:`Request`. 

485 :param headers: (optional) Dictionary of HTTP Headers to send with the 

486 :class:`Request`. 

487 :param cookies: (optional) Dict or CookieJar object to send with the 

488 :class:`Request`. 

489 :param files: (optional) Dictionary of ``'filename': file-like-objects`` 

490 for multipart encoding upload. 

491 :param auth: (optional) Auth tuple or callable to enable 

492 Basic/Digest/Custom HTTP Auth. 

493 :param timeout: (optional) How long to wait for the server to send 

494 data before giving up, as a float, or a :ref:`(connect timeout, 

495 read timeout) <timeouts>` tuple. 

496 :type timeout: float or tuple 

497 :param allow_redirects: (optional) Set to True by default. 

498 :type allow_redirects: bool 

499 :param proxies: (optional) Dictionary mapping protocol or protocol and 

500 hostname to the URL of the proxy. 

501 :param stream: (optional) whether to immediately download the response 

502 content. Defaults to ``False``. 

503 :param verify: (optional) Either a boolean, in which case it controls whether we verify 

504 the server's TLS certificate, or a string, in which case it must be a path 

505 to a CA bundle to use. Defaults to ``True``. When set to 

506 ``False``, requests will accept any TLS certificate presented by 

507 the server, and will ignore hostname mismatches and/or expired 

508 certificates, which will make your application vulnerable to 

509 man-in-the-middle (MitM) attacks. Setting verify to ``False``  

510 may be useful during local development or testing. 

511 :param cert: (optional) if String, path to ssl client cert file (.pem). 

512 If Tuple, ('cert', 'key') pair. 

513 :rtype: requests.Response 

514 """ 

515 # Create the Request. 

516 req = Request( 

517 method=method.upper(), 

518 url=url, 

519 headers=headers, 

520 files=files, 

521 data=data or {}, 

522 json=json, 

523 params=params or {}, 

524 auth=auth, 

525 cookies=cookies, 

526 hooks=hooks, 

527 ) 

528 prep = self.prepare_request(req) 

529 

530 proxies = proxies or {} 

531 

532 settings = self.merge_environment_settings( 

533 prep.url, proxies, stream, verify, cert 

534 ) 

535 

536 # Send the request. 

537 send_kwargs = { 

538 'timeout': timeout, 

539 'allow_redirects': allow_redirects, 

540 } 

541 send_kwargs.update(settings) 

542 resp = self.send(prep, **send_kwargs) 

543 

544 return resp 

545 

546 def get(self, url, **kwargs): 

547 r"""Sends a GET request. Returns :class:`Response` object. 

548 

549 :param url: URL for the new :class:`Request` object. 

550 :param \*\*kwargs: Optional arguments that ``request`` takes. 

551 :rtype: requests.Response 

552 """ 

553 

554 kwargs.setdefault('allow_redirects', True) 

555 return self.request('GET', url, **kwargs) 

556 

557 def options(self, url, **kwargs): 

558 r"""Sends a OPTIONS request. Returns :class:`Response` object. 

559 

560 :param url: URL for the new :class:`Request` object. 

561 :param \*\*kwargs: Optional arguments that ``request`` takes. 

562 :rtype: requests.Response 

563 """ 

564 

565 kwargs.setdefault('allow_redirects', True) 

566 return self.request('OPTIONS', url, **kwargs) 

567 

568 def head(self, url, **kwargs): 

569 r"""Sends a HEAD request. Returns :class:`Response` object. 

570 

571 :param url: URL for the new :class:`Request` object. 

572 :param \*\*kwargs: Optional arguments that ``request`` takes. 

573 :rtype: requests.Response 

574 """ 

575 

576 kwargs.setdefault('allow_redirects', False) 

577 return self.request('HEAD', url, **kwargs) 

578 

579 def post(self, url, data=None, json=None, **kwargs): 

580 r"""Sends a POST request. Returns :class:`Response` object. 

581 

582 :param url: URL for the new :class:`Request` object. 

583 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

584 object to send in the body of the :class:`Request`. 

585 :param json: (optional) json to send in the body of the :class:`Request`. 

586 :param \*\*kwargs: Optional arguments that ``request`` takes. 

587 :rtype: requests.Response 

588 """ 

589 

590 return self.request('POST', url, data=data, json=json, **kwargs) 

591 

592 def put(self, url, data=None, **kwargs): 

593 r"""Sends a PUT request. Returns :class:`Response` object. 

594 

595 :param url: URL for the new :class:`Request` object. 

596 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

597 object to send in the body of the :class:`Request`. 

598 :param \*\*kwargs: Optional arguments that ``request`` takes. 

599 :rtype: requests.Response 

600 """ 

601 

602 return self.request('PUT', url, data=data, **kwargs) 

603 

604 def patch(self, url, data=None, **kwargs): 

605 r"""Sends a PATCH request. Returns :class:`Response` object. 

606 

607 :param url: URL for the new :class:`Request` object. 

608 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

609 object to send in the body of the :class:`Request`. 

610 :param \*\*kwargs: Optional arguments that ``request`` takes. 

611 :rtype: requests.Response 

612 """ 

613 

614 return self.request('PATCH', url, data=data, **kwargs) 

615 

616 def delete(self, url, **kwargs): 

617 r"""Sends a DELETE request. Returns :class:`Response` object. 

618 

619 :param url: URL for the new :class:`Request` object. 

620 :param \*\*kwargs: Optional arguments that ``request`` takes. 

621 :rtype: requests.Response 

622 """ 

623 

624 return self.request('DELETE', url, **kwargs) 

625 

626 def send(self, request, **kwargs): 

627 """Send a given PreparedRequest. 

628 

629 :rtype: requests.Response 

630 """ 

631 # Set defaults that the hooks can utilize to ensure they always have 

632 # the correct parameters to reproduce the previous request. 

633 kwargs.setdefault('stream', self.stream) 

634 kwargs.setdefault('verify', self.verify) 

635 kwargs.setdefault('cert', self.cert) 

636 kwargs.setdefault('proxies', self.proxies) 

637 

638 # It's possible that users might accidentally send a Request object. 

639 # Guard against that specific failure case. 

640 if isinstance(request, Request): 

641 raise ValueError('You can only send PreparedRequests.') 

642 

643 # Set up variables needed for resolve_redirects and dispatching of hooks 

644 allow_redirects = kwargs.pop('allow_redirects', True) 

645 stream = kwargs.get('stream') 

646 hooks = request.hooks 

647 

648 # Get the appropriate adapter to use 

649 adapter = self.get_adapter(url=request.url) 

650 

651 # Start time (approximately) of the request 

652 start = preferred_clock() 

653 

654 # Send the request 

655 r = adapter.send(request, **kwargs) 

656 

657 # Total elapsed time of the request (approximately) 

658 elapsed = preferred_clock() - start 

659 r.elapsed = timedelta(seconds=elapsed) 

660 

661 # Response manipulation hooks 

662 r = dispatch_hook('response', hooks, r, **kwargs) 

663 

664 # Persist cookies 

665 if r.history: 

666 

667 # If the hooks create history then we want those cookies too 

668 for resp in r.history: 

669 extract_cookies_to_jar(self.cookies, resp.request, resp.raw) 

670 

671 extract_cookies_to_jar(self.cookies, request, r.raw) 

672 

673 # Resolve redirects if allowed. 

674 if allow_redirects: 

675 # Redirect resolving generator. 

676 gen = self.resolve_redirects(r, request, **kwargs) 

677 history = [resp for resp in gen] 

678 else: 

679 history = [] 

680 

681 # Shuffle things around if there's history. 

682 if history: 

683 # Insert the first (original) request at the start 

684 history.insert(0, r) 

685 # Get the last request made 

686 r = history.pop() 

687 r.history = history 

688 

689 # If redirects aren't being followed, store the response on the Request for Response.next(). 

690 if not allow_redirects: 

691 try: 

692 r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) 

693 except StopIteration: 

694 pass 

695 

696 if not stream: 

697 r.content 

698 

699 return r 

700 

701 def merge_environment_settings(self, url, proxies, stream, verify, cert): 

702 """ 

703 Check the environment and merge it with some settings. 

704 

705 :rtype: dict 

706 """ 

707 # Gather clues from the surrounding environment. 

708 if self.trust_env: 

709 # Set environment's proxies. 

710 no_proxy = proxies.get('no_proxy') if proxies is not None else None 

711 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) 

712 for (k, v) in env_proxies.items(): 

713 proxies.setdefault(k, v) 

714 

715 # Look for requests environment configuration and be compatible 

716 # with cURL. 

717 if verify is True or verify is None: 

718 verify = (os.environ.get('REQUESTS_CA_BUNDLE') or 

719 os.environ.get('CURL_CA_BUNDLE')) 

720 

721 # Merge all the kwargs. 

722 proxies = merge_setting(proxies, self.proxies) 

723 stream = merge_setting(stream, self.stream) 

724 verify = merge_setting(verify, self.verify) 

725 cert = merge_setting(cert, self.cert) 

726 

727 return {'verify': verify, 'proxies': proxies, 'stream': stream, 

728 'cert': cert} 

729 

730 def get_adapter(self, url): 

731 """ 

732 Returns the appropriate connection adapter for the given URL. 

733 

734 :rtype: requests.adapters.BaseAdapter 

735 """ 

736 for (prefix, adapter) in self.adapters.items(): 

737 

738 if url.lower().startswith(prefix.lower()): 

739 return adapter 

740 

741 # Nothing matches :-/ 

742 raise InvalidSchema("No connection adapters were found for {!r}".format(url)) 

743 

744 def close(self): 

745 """Closes all adapters and as such the session""" 

746 for v in self.adapters.values(): 

747 v.close() 

748 

749 def mount(self, prefix, adapter): 

750 """Registers a connection adapter to a prefix. 

751 

752 Adapters are sorted in descending order by prefix length. 

753 """ 

754 self.adapters[prefix] = adapter 

755 keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] 

756 

757 for key in keys_to_move: 

758 self.adapters[key] = self.adapters.pop(key) 

759 

760 def __getstate__(self): 

761 state = {attr: getattr(self, attr, None) for attr in self.__attrs__} 

762 return state 

763 

764 def __setstate__(self, state): 

765 for attr, value in state.items(): 

766 setattr(self, attr, value) 

767 

768 

769def session(): 

770 """ 

771 Returns a :class:`Session` for context-management. 

772 

773 .. deprecated:: 1.0.0 

774 

775 This method has been deprecated since version 1.0.0 and is only kept for 

776 backwards compatibility. New code should use :class:`~requests.sessions.Session` 

777 to create a session. This may be removed at a future date. 

778 

779 :rtype: Session 

780 """ 

781 return Session()