You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
11 KiB

# -*- coding: utf-8 -*-
"""
requests.session
~~~~~~~~~~~~~~~~
This module provides a Session object to manage and persist settings across
requests (cookies, auth, proxies).
"""
13 years ago
from copy import deepcopy
from .compat import cookielib
from .cookies import cookiejar_from_dict, remove_cookie_by_name
from .defaults import defaults
from .models import Request
from .hooks import dispatch_hook
13 years ago
from .utils import header_expand, from_key_val_list
from .packages.urllib3.poolmanager import PoolManager
13 years ago
def merge_kwargs(local_kwarg, default_kwarg):
"""Merges kwarg dictionaries.
If a local key in the dictionary is set to None, it will be removed.
"""
if default_kwarg is None:
return local_kwarg
13 years ago
if isinstance(local_kwarg, str):
return local_kwarg
if local_kwarg is None:
return default_kwarg
# Bypass if not a dictionary (e.g. timeout)
if not hasattr(default_kwarg, 'items'):
return local_kwarg
13 years ago
default_kwarg = from_key_val_list(default_kwarg)
local_kwarg = from_key_val_list(local_kwarg)
# Update new values.
kwargs = default_kwarg.copy()
kwargs.update(local_kwarg)
# Remove keys that are set to None.
13 years ago
for (k, v) in local_kwarg.items():
if v is None:
del kwargs[k]
return kwargs
class Session(object):
"""A Requests session."""
__attrs__ = [
'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks',
13 years ago
'params', 'config', 'verify', 'cert', 'prefetch']
def __init__(self,
headers=None,
cookies=None,
auth=None,
timeout=None,
proxies=None,
hooks=None,
params=None,
14 years ago
config=None,
13 years ago
prefetch=True,
13 years ago
verify=True,
cert=None):
13 years ago
self.headers = from_key_val_list(headers or [])
self.auth = auth
self.timeout = timeout
13 years ago
self.proxies = from_key_val_list(proxies or [])
self.hooks = from_key_val_list(hooks or {})
self.params = from_key_val_list(params or [])
self.config = from_key_val_list(config or {})
13 years ago
self.prefetch = prefetch
14 years ago
self.verify = verify
13 years ago
self.cert = cert
13 years ago
for (k, v) in list(defaults.items()):
13 years ago
self.config.setdefault(k, deepcopy(v))
13 years ago
self.init_poolmanager()
# Set up a CookieJar to be used by default
13 years ago
if isinstance(cookies, cookielib.CookieJar):
self.cookies = cookies
else:
self.cookies = cookiejar_from_dict(cookies)
13 years ago
def init_poolmanager(self):
self.poolmanager = PoolManager(
num_pools=self.config.get('pool_connections'),
maxsize=self.config.get('pool_maxsize')
)
def __repr__(self):
return '<requests-client at 0x%x>' % (id(self))
def __enter__(self):
return self
def __exit__(self, *args):
13 years ago
self.close()
def close(self):
"""Dispose of any internal state.
Currently, this just closes the PoolManager, which closes pooled
connections.
"""
self.poolmanager.clear()
def request(self, method, url,
params=None,
data=None,
headers=None,
cookies=None,
files=None,
auth=None,
timeout=None,
13 years ago
allow_redirects=True,
proxies=None,
hooks=None,
return_response=True,
config=None,
13 years ago
prefetch=None,
13 years ago
verify=None,
cert=None):
"""Constructs and sends a :class:`Request <Request>`.
Returns :class:`Response <Response>` object.
:param method: method for the new :class:`Request` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
13 years ago
:param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
13 years ago
:param allow_redirects: (optional) Boolean. Set to True by default.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
:param return_response: (optional) If False, an un-sent Request object will returned.
13 years ago
:param config: (optional) A configuration dictionary. See ``request.defaults`` for allowed keys and their default values.
13 years ago
:param prefetch: (optional) whether to immediately download the response content. Defaults to ``True``.
14 years ago
:param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
13 years ago
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
"""
method = str(method).upper()
# Default empty dicts for dict params.
13 years ago
data = [] if data is None else data
files = [] if files is None else files
headers = {} if headers is None else headers
params = {} if params is None else params
hooks = {} if hooks is None else hooks
13 years ago
prefetch = prefetch if prefetch is not None else self.prefetch
14 years ago
# use session's hooks as defaults
13 years ago
for key, cb in list(self.hooks.items()):
hooks.setdefault(key, cb)
# Expand header values.
if headers:
13 years ago
for k, v in list(headers.items() or {}):
headers[k] = header_expand(v)
args = dict(
method=method,
url=url,
data=data,
13 years ago
params=from_key_val_list(params),
headers=from_key_val_list(headers),
cookies=cookies,
files=files,
auth=auth,
13 years ago
hooks=from_key_val_list(hooks),
timeout=timeout,
allow_redirects=allow_redirects,
13 years ago
proxies=from_key_val_list(proxies),
config=from_key_val_list(config),
13 years ago
prefetch=prefetch,
14 years ago
verify=verify,
13 years ago
cert=cert,
_poolmanager=self.poolmanager
)
13 years ago
# merge session cookies into passed-in ones
dead_cookies = None
# passed-in cookies must become a CookieJar:
if not isinstance(cookies, cookielib.CookieJar):
args['cookies'] = cookiejar_from_dict(cookies)
# support unsetting cookies that have been passed in with None values
# this is only meaningful when `cookies` is a dict ---
# for a real CookieJar, the client should use session.cookies.clear()
if cookies is not None:
dead_cookies = [name for name in cookies if cookies[name] is None]
# merge the session's cookies into the passed-in cookies:
for cookie in self.cookies:
args['cookies'].set_cookie(cookie)
# remove the unset cookies from the jar we'll be using with the current request
# (but not from the session's own store of cookies):
if dead_cookies is not None:
for name in dead_cookies:
remove_cookie_by_name(args['cookies'], name)
# Merge local kwargs with session kwargs.
for attr in self.__attrs__:
13 years ago
# we already merged cookies:
if attr == 'cookies':
continue
session_val = getattr(self, attr, None)
local_val = args.get(attr)
args[attr] = merge_kwargs(local_val, session_val)
# Arguments manipulation hook.
args = dispatch_hook('args', args['hooks'], args)
# Create the (empty) response.
r = Request(**args)
# Give the response some context.
r.session = self
# Don't send if asked nicely.
if not return_response:
return r
# Send the HTTP Request.
r.send(prefetch=prefetch)
# Return the response.
return r.response
def get(self, url, **kwargs):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', True)
return self.request('get', url, **kwargs)
def options(self, url, **kwargs):
"""Sends a OPTIONS request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', True)
return self.request('options', url, **kwargs)
def head(self, url, **kwargs):
"""Sends a HEAD request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
13 years ago
kwargs.setdefault('allow_redirects', False)
return self.request('head', url, **kwargs)
def post(self, url, data=None, **kwargs):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
return self.request('post', url, data=data, **kwargs)
def put(self, url, data=None, **kwargs):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
return self.request('put', url, data=data, **kwargs)
def patch(self, url, data=None, **kwargs):
"""Sends a PATCH request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
return self.request('patch', url, data=data, **kwargs)
def delete(self, url, **kwargs):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
13 years ago
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
return self.request('delete', url, **kwargs)
13 years ago
def __getstate__(self):
return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)
def __setstate__(self, state):
for attr, value in state.items():
setattr(self, attr, value)
self.init_poolmanager()
def session(**kwargs):
"""Returns a :class:`Session` for context-management."""
return Session(**kwargs)