diff --git a/AUTHORS b/AUTHORS index 2d34a221..61874982 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,3 +35,7 @@ Patches and Suggestions - Jens Diemer - Alex <@alopatin> - Tom Hogans +- Armin Ronacher +- Shrikant Sharat Kandula +- Mikko Ohtamaa +- Den Shabalin \ No newline at end of file diff --git a/AUTHORS.orig b/AUTHORS.orig new file mode 100644 index 00000000..dd88224b --- /dev/null +++ b/AUTHORS.orig @@ -0,0 +1,40 @@ +Requests is written and maintained by Kenneth Reitz and +various contributors: + +Development Lead +```````````````` + +- Kenneth Reitz + + +Patches and Suggestions +``````````````````````` + +- Various Pocoo Members +- Chris Adams +- Flavio Percoco Premoli +- Dj Gilcrease +- Justin Murphy +- Rob Madole +- Aram Dulyan +- Johannes Gorset +- 村山めがね (Megane Murayama) +- James Rowe +- Daniel Schauenberg +- Zbigniew Siciarz +- Daniele Tricoli 'Eriol' +- Richard Boulton +- Miguel Olivares +- Alberto Paro +- Jérémy Bethmont +- 潘旭 (Xu Pan) +- Tamás Gulácsi +- Rubén Abad +- Peter Manser +- Jeremy Selie +<<<<<<< HEAD +- Jens Diemer +- Alex <@alopatin> +======= +- Tom Hogans +>>>>>>> 0ed641a26ec2200de00e4bbf3d170c767375351e diff --git a/HISTORY.rst b/HISTORY.rst index b2ae8f9f..b09392b5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,17 @@ History ------- +0.6.1 (2011-08-20) +++++++++++++++++++ + +* Enhanced status codes experience ``\o/`` +* Set a maximum number of redirects (``settings.max_redirects``) +* Full Unicode URL support +* Support for protocol-less redirects. +* Allow for arbitrary request types. +* Bugfixes + + 0.6.0 (2011-08-17) ++++++++++++++++++ diff --git a/README.rst b/README.rst index 529d1445..3f6455ed 100644 --- a/README.rst +++ b/README.rst @@ -100,11 +100,5 @@ Contribute If you'd like to contribute, simply fork `the repository`_, commit your changes to the **develop** branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS_. - -Roadmap -------- - -- Sphinx Documentation - .. _`the repository`: http://github.com/kennethreitz/requests .. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index cdf1abb6..d145da21 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -34,7 +34,7 @@ Sessions can also be used to provide default data to the request methods:: with requests.session(auth=auth, headers=headers) as c: # both 'x-test' and 'x-test2' are sent - c.get('http://httpbin.org/headers', header={'x-test2', 'true'}) + c.get('http://httpbin.org/headers', headers={'x-test2': 'true'}) .. admonition:: Global Settings diff --git a/requests/config.py b/requests/config.py index a42f15d2..39be2ed4 100644 --- a/requests/config.py +++ b/requests/config.py @@ -61,6 +61,7 @@ settings.accept_gzip = True settings.proxies = None settings.verbose = None settings.timeout = None +settings.max_redirects = 30 #: Use socket.setdefaulttimeout() as fallback? settings.timeout_fallback = True diff --git a/requests/core.py b/requests/core.py index 68e53923..8ba34a2f 100644 --- a/requests/core.py +++ b/requests/core.py @@ -12,8 +12,8 @@ This module implements the main Requests system. """ __title__ = 'requests' -__version__ = '0.6.0' -__build__ = 0x000600 +__version__ = '0.6.1' +__build__ = 0x000601 __author__ = 'Kenneth Reitz' __license__ = 'ISC' __copyright__ = 'Copyright 2011 Kenneth Reitz' diff --git a/requests/exceptions.py b/requests/exceptions.py index eff7512a..c08c6148 100644 --- a/requests/exceptions.py +++ b/requests/exceptions.py @@ -21,3 +21,6 @@ class URLRequired(RequestException): class InvalidMethod(RequestException): """An inappropriate method was attempted.""" + +class TooManyRedirects(RequestException): + """Too many redirects.""" diff --git a/requests/models.py b/requests/models.py index d6b058fb..2d7fc8fe 100644 --- a/requests/models.py +++ b/requests/models.py @@ -21,10 +21,12 @@ from .structures import CaseInsensitiveDict from .packages.poster.encode import multipart_encode from .packages.poster.streaminghttp import register_openers, get_handlers from .utils import dict_from_cookiejar -from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod +from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects +from .status_codes import codes -REDIRECT_STATI = (301, 302, 303, 307) +REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved) + class Request(object): @@ -32,8 +34,6 @@ class Request(object): Requests. Recommended interface is with the Requests functions. """ - _METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH') - def __init__(self, url=None, headers=dict(), files=None, method=None, data=dict(), params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False, @@ -116,14 +116,6 @@ class Request(object): return '' % (self.method) - def __setattr__(self, name, value): - if (name == 'method') and (value): - if not value in self._METHODS: - raise InvalidMethod() - - object.__setattr__(self, name, value) - - def _checks(self): """Deterministic checks for consistency.""" @@ -184,7 +176,8 @@ class Request(object): try: response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None)) response.read = resp.read - response.close = resp.close + response._resp = resp + response._close = resp.close if self.cookiejar: @@ -211,20 +204,31 @@ class Request(object): while ( ('location' in r.headers) and ((self.method in ('GET', 'HEAD')) or - (r.status_code is 303) or + (r.status_code is codes.see_other) or (self.allow_redirects)) ): + r.close() + + if not len(history) < settings.max_redirects: + raise TooManyRedirects() + history.append(r) url = r.headers['location'] + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith('//'): + parsed_rurl = urlparse(r.url) + url = '%s:%s' % (parsed_rurl.scheme, url) + # Facilitate non-RFC2616-compliant 'location' headers # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') - url = urljoin(r.url, url) + if not urlparse(url).netloc: + url = urljoin(r.url, urllib.quote(urllib.unquote(url))) # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 - if r.status_code is 303: + if r.status_code is codes.see_other: method = 'GET' else: method = self.method @@ -269,10 +273,13 @@ class Request(object): def _build_url(self): """Build the actual URL to use.""" - # Support for unicode domain names. - parsed_url = list(urlparse(self.url)) - parsed_url[1] = parsed_url[1].encode('idna') - self.url = urlunparse(parsed_url) + # Support for unicode domain names and paths. + scheme, netloc, path, params, query, fragment = urlparse(self.url) + netloc = netloc.encode('idna') + if isinstance(path, unicode): + path = path.encode('utf-8') + path = urllib.quote(urllib.unquote(path)) + self.url = str(urlunparse([ scheme, netloc, path, params, query, fragment ])) if self._enc_params: if urlparse(self.url).query: @@ -426,7 +433,8 @@ class Response(object): except zlib.error: pass return self._content - + else: + raise AttributeError def raise_for_status(self): """Raises stored :class:`HTTPError` or :class:`URLError`, if one occured.""" @@ -434,6 +442,11 @@ class Response(object): raise self.error + def close(self): + if self._resp.fp is not None and hasattr(self._resp.fp, '_sock'): + self._resp.fp._sock.recv = None + self._close() + class AuthManager(object): """Requests Authentication Manager.""" diff --git a/requests/status_codes.py b/requests/status_codes.py index e8023142..a809de6a 100644 --- a/requests/status_codes.py +++ b/requests/status_codes.py @@ -22,9 +22,9 @@ _codes = { # Redirection. 300: ('multiple_choices',), - 301: ('moved_permanently', 'moved'), + 301: ('moved_permanently', 'moved', '\\o-'), 302: ('found',), - 302: ('see_other', 'other'), + 303: ('see_other', 'other'), 304: ('not_modified',), 305: ('use_proxy',), 306: ('switch_proxy',), @@ -36,7 +36,7 @@ _codes = { 401: ('unauthorized',), 402: ('payment_required', 'payment'), 403: ('forbidden',), - 404: ('not_found',), + 404: ('not_found', '-o-'), 405: ('method_not_allowed', 'not_allowed'), 406: ('not_acceptable',), 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'), @@ -62,7 +62,7 @@ _codes = { 499: ('client_closed_request',), # Server Error. - 500: ('internal_server_error', 'server_error'), + 500: ('internal_server_error', 'server_error', '/o\\'), 501: ('not_implemented',), 502: ('bad_gateway',), 503: ('service_unavailable', 'unavailable'),