From dbf70fc40fd0d05133bf93d0d43d1267650ccf78 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:00:26 -0400 Subject: [PATCH 1/7] All methods are valid now --- requests/__init__.py | 2 +- requests/exceptions.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/requests/__init__.py b/requests/__init__.py index b443c95ede..630ce6a5a1 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -30,5 +30,5 @@ from .config import settings from .exceptions import ( RequestException, AuthenticationError, Timeout, URLRequired, - InvalidMethod, TooManyRedirects + TooManyRedirects ) diff --git a/requests/exceptions.py b/requests/exceptions.py index 13305e500e..e381e8cf39 100644 --- a/requests/exceptions.py +++ b/requests/exceptions.py @@ -12,15 +12,12 @@ class RequestException(Exception): class AuthenticationError(RequestException): """The authentication credentials provided were invalid.""" - + class Timeout(RequestException): """The request timed out.""" class URLRequired(RequestException): """A valid URL is required to make a request.""" -class InvalidMethod(RequestException): - """An inappropriate method was attempted.""" - class TooManyRedirects(RequestException): """Too many redirects.""" From 77909f1d4999dcc988acdae043da406031dd5903 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:07:00 -0400 Subject: [PATCH 2/7] v0.6.7 --- HISTORY.rst | 2 ++ requests/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 1c8398c3c1..a0666c2374 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,8 @@ History 0.6.7 +++++ +* Sessions are now the primary interface. +* Deprecated InvalidMethodException. * PATCH fix. diff --git a/requests/__init__.py b/requests/__init__.py index 630ce6a5a1..2b25c9e29f 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -15,8 +15,8 @@ """ __title__ = 'requests' -__version__ = '0.6.6' -__build__ = 0x000606 +__version__ = '0.6.7' +__build__ = 0x000607 __author__ = 'Kenneth Reitz' __license__ = 'ISC' __copyright__ = 'Copyright 2011 Kenneth Reitz' From d552e6c8847fb1a32880f1a2912af9b472f0f40d Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:07:14 -0400 Subject: [PATCH 3/7] pep8 all the things --- test_requests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_requests.py b/test_requests.py index 188cbc0933..37d2a7d9ae 100755 --- a/test_requests.py +++ b/test_requests.py @@ -17,7 +17,6 @@ import json - # TODO: Detect an open port. PORT = os.environ.get('HTTPBIN_PORT', '7077') @@ -47,8 +46,9 @@ def setUp(self): if not _httpbin: - self.httpbin = envoy.connect('gunicorn httpbin:app --bind=0.0.0.0:%s' % (PORT)) + c = envoy.connect('gunicorn httpbin:app --bind=0.0.0.0:%s' % (PORT)) + self.httpbin = c _httpbin = True time.sleep(1) From d72d1162142d1bf8b1b5711c664fbbd674f349d1 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:10:18 -0400 Subject: [PATCH 4/7] Move request and get to session --- requests/api.py | 72 +++---------------------- requests/sessions.py | 123 ++++++++++++++++++++++++++++++------------- 2 files changed, 92 insertions(+), 103 deletions(-) diff --git a/requests/api.py b/requests/api.py index 41240f00f8..b737cfe954 100644 --- a/requests/api.py +++ b/requests/api.py @@ -16,84 +16,24 @@ from .status_codes import codes from .hooks import dispatch_hook from .utils import cookiejar_from_dict, header_expand - +from .sessions import session __all__ = ('request', 'get', 'head', 'post', 'patch', 'put', 'delete') + def request(method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=False, proxies=None, hooks=None, return_response=True): - """Constructs and sends a :class:`Request `. - Returns :class:`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. - :param auth: (optional) AuthObject to enable Basic HTTP Auth. - :param timeout: (optional) Float describing the timeout of the request. - :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. - :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. - """ - - method = str(method).upper() - - if cookies is None: - cookies = {} - - cookies = cookiejar_from_dict(cookies) - - # Expand header values - if headers: - for k, v in headers.items() or {}: - headers[k] = header_expand(v) - - args = dict( - method = method, - url = url, - data = data, - params = params, - headers = headers, - cookiejar = cookies, - files = files, - auth = auth, - hooks = hooks, - timeout = timeout or config.settings.timeout, - allow_redirects = allow_redirects, - proxies = proxies or config.settings.proxies, + s = session() + return s.request( + method, url, params, data, headers, cookies, files, auth, + timeout, allow_redirects, proxies, hooks, return_response ) - # Arguments manipulation hook. - args = dispatch_hook('args', hooks, args) - - r = Request(**args) - - # Pre-request hook. - r = dispatch_hook('pre_request', hooks, r) - - # Don't send if asked nicely. - if not return_response: - return r - - # Send the HTTP Request. - r.send() - - # Post-request hook. - r = dispatch_hook('post_request', hooks, r) - - # Response manipulation hook. - r.response = dispatch_hook('response', hooks, r.response) - - return r.response def get(url, **kwargs): - """Sends a GET request. Returns :class:`Response` object. :param url: URL for the new :class:`Request` object. diff --git a/requests/sessions.py b/requests/sessions.py index 64ef7b2f9e..0dc5e9d664 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -11,9 +11,10 @@ import cookielib -from . import api -from .utils import add_dict_to_cookiejar - +from . import config +from .models import Request +from .hooks import dispatch_hook +from .utils import add_dict_to_cookiejar, cookiejar_from_dict, header_expand def merge_kwargs(local_kwarg, default_kwarg): @@ -25,6 +26,9 @@ def merge_kwargs(local_kwarg, default_kwarg): if default_kwarg is None: return local_kwarg + if isinstance(local_kwarg, basestring): + return local_kwarg + if local_kwarg is None: return default_kwarg @@ -32,8 +36,6 @@ def merge_kwargs(local_kwarg, default_kwarg): if not hasattr(default_kwarg, 'items'): return local_kwarg - - # Update new values. kwargs = default_kwarg.copy() kwargs.update(local_kwarg) @@ -73,7 +75,7 @@ def __init__(self, self.cookies = cookielib.FileCookieJar() # Map and wrap requests.api methods - self._map_api_methods() + # self._map_api_methods() def __repr__(self): @@ -85,48 +87,95 @@ def __enter__(self): def __exit__(self, *args): pass - def _map_api_methods(self): - """Reads each available method from requests.api and decorates - them with a wrapper, which inserts any instance-local attributes - (from __attrs__) that have been set, combining them with **kwargs. + def request(self, method, url, + params=None, data=None, headers=None, cookies=None, files=None, auth=None, + timeout=None, allow_redirects=False, proxies=None, hooks=None, return_response=True): + + """Constructs and sends a :class:`Request `. + Returns :class:`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. + :param auth: (optional) AuthObject to enable Basic HTTP Auth. + :param timeout: (optional) Float describing the timeout of the request. + :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. + :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. """ - def pass_args(func): - def wrapper_func(*args, **kwargs): + method = str(method).upper() + + if cookies is None: + cookies = {} + + if isinstance(cookies, dict): + cookies = add_dict_to_cookiejar(self.cookies, cookies) + + cookies = cookiejar_from_dict(cookies) - # Argument collector. - _kwargs = {} + # Expand header values + if headers: + for k, v in headers.items() or {}: + headers[k] = header_expand(v) - # If a session request has a cookie_dict, inject the - # values into the existing CookieJar instead. - if isinstance(kwargs.get('cookies', None), dict): - kwargs['cookies'] = add_dict_to_cookiejar( - self.cookies, kwargs['cookies'] - ) + args = dict( + method = method, + url = url, + data = data, + params = params, + headers = headers, + cookies = cookies, + files = files, + auth = auth, + hooks = hooks, + timeout = timeout or config.settings.timeout, + allow_redirects = allow_redirects, + proxies = proxies or config.settings.proxies, + ) - for attr in self.__attrs__: - # for attr in ['headers',]: - s_val = self.__dict__.get(attr) - r_val = kwargs.get(attr) + for attr in self.__attrs__: + session_val = getattr(self, attr, None) + local_val = args.get(attr) - new_attr = merge_kwargs(r_val, s_val) + args[attr] = merge_kwargs(local_val, session_val) - # Skip attributes that were set to None. - if new_attr is not None: - _kwargs[attr] = new_attr + # Arguments manipulation hook. + args = dispatch_hook('args', hooks, args) - # Make sure we didn't miss anything. - for (k, v) in kwargs.items(): - if k not in _kwargs: - _kwargs[k] = v + r = Request(**args) - return func(*args, **_kwargs) + # Pre-request hook. + r = dispatch_hook('pre_request', hooks, r) - return wrapper_func + # Don't send if asked nicely. + if not return_response: + return r + + # Send the HTTP Request. + r.send() + + # Post-request hook. + r = dispatch_hook('post_request', hooks, r) + + # Response manipulation hook. + r.response = dispatch_hook('response', hooks, r.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. + :param **kwargs: Optional arguments that ``request`` takes. + """ - # Map and decorate each function available in requests.api - map(lambda fn: setattr(self, fn, pass_args(getattr(api, fn))), - api.__all__) + kwargs.setdefault('allow_redirects', True) + return self.request('GET', url, **kwargs) def session(**kwargs): From f269c6c89a1cf0b6368f58a6c2cc4d095d1f5239 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:10:47 -0400 Subject: [PATCH 5/7] cookiejar => cookies in models --- requests/models.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/requests/models.py b/requests/models.py index 3c952c2537..d58e4ad83e 100644 --- a/requests/models.py +++ b/requests/models.py @@ -24,7 +24,7 @@ from .packages.poster.streaminghttp import register_openers, get_handlers from .utils import dict_from_cookiejar, get_unicode_from_response, stream_decode_response_unicode, decode_gzip, stream_decode_gzip from .status_codes import codes -from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects +from .exceptions import Timeout, URLRequired, TooManyRedirects REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved) @@ -38,7 +38,7 @@ class Request(object): def __init__(self, url=None, headers=dict(), files=None, method=None, data=dict(), - params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False, + params=dict(), auth=None, cookies=None, timeout=None, redirect=False, allow_redirects=False, proxies=None, hooks=None): #: Float describes the timeout of the request. @@ -91,7 +91,7 @@ def __init__(self, self.auth = auth #: CookieJar to attach to :class:`Request `. - self.cookiejar = cookiejar + self.cookies = cookies #: True if Request has been sent. self.sent = False @@ -132,8 +132,8 @@ def _get_opener(self): _handlers = [] - if self.cookiejar is not None: - _handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar)) + if self.cookies is not None: + _handlers.append(urllib2.HTTPCookieProcessor(self.cookies)) if self.auth: if not isinstance(self.auth.handler, @@ -191,8 +191,8 @@ def build(resp): response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None)) response.raw = resp - if self.cookiejar: - response.cookies = dict_from_cookiejar(self.cookiejar) + if self.cookies: + response.cookies = dict_from_cookiejar(self.cookies) except AttributeError: @@ -244,7 +244,7 @@ def build(resp): request = Request( url, self.headers, self.files, method, - self.data, self.params, self.auth, self.cookiejar, + self.data, self.params, self.auth, self.cookies, redirect=True ) request.send() @@ -364,8 +364,8 @@ def send(self, anyway=False): # restore global timeout socket.setdefaulttimeout(old_timeout) - if self.cookiejar is not None: - self.cookiejar.extract_cookies(resp, req) + if self.cookies is not None: + self.cookies.extract_cookies(resp, req) except (urllib2.HTTPError, urllib2.URLError), why: if hasattr(why, 'reason'): From 597ace5718eb116819966213e8772458a9141c29 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:13:22 -0400 Subject: [PATCH 6/7] import cleanups --- requests/api.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/requests/api.py b/requests/api.py index b737cfe954..f44f35bf1e 100644 --- a/requests/api.py +++ b/requests/api.py @@ -11,11 +11,6 @@ """ -from . import config -from .models import Request, Response, AuthObject -from .status_codes import codes -from .hooks import dispatch_hook -from .utils import cookiejar_from_dict, header_expand from .sessions import session __all__ = ('request', 'get', 'head', 'post', 'patch', 'put', 'delete') @@ -40,7 +35,6 @@ def get(url, **kwargs): :param **kwargs: Optional arguments that ``request`` takes. """ - kwargs.setdefault('allow_redirects', True) return request('GET', url, **kwargs) From 2b3bd78685f9d30bf29e090b58f5aa1a3b799cdf Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 22 Oct 2011 18:15:28 -0400 Subject: [PATCH 7/7] Add all methods to session --- requests/sessions.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/requests/sessions.py b/requests/sessions.py index 0dc5e9d664..1d130a342b 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -167,6 +167,7 @@ def request(self, method, url, return r.response + def get(self, url, **kwargs): """Sends a GET request. Returns :class:`Response` object. @@ -178,6 +179,61 @@ def get(self, url, **kwargs): return self.request('GET', url, **kwargs) + def head(self, url, **kwargs): + """Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param **kwargs: Optional arguments that ``request`` takes. + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('HEAD', url, **kwargs) + + + def post(self, url, data='', **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`. + :param **kwargs: Optional arguments that ``request`` takes. + """ + + return self.request('post', url, data=data, **kwargs) + + + def put(self, url, data='', **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`. + :param **kwargs: Optional arguments that ``request`` takes. + """ + + return self.request('put', url, data=data, **kwargs) + + + def patch(url, data='', **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`. + :param **kwargs: Optional arguments that ``request`` takes. + """ + + return self.request('patch', url, data='', **kwargs) + + + def delete(self, url, **kwargs): + """Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param **kwargs: Optional arguments that ``request`` takes. + """ + + return self.request('delete', url, **kwargs) + + + def session(**kwargs): """Returns a :class:`Session` for context-management."""