Compare commits

...

42 Commits

Author SHA1 Message Date
Kenneth Reitz
8754bdded9 authors update 2012-01-18 21:39:43 -05:00
Kenneth Reitz
49d79546e8 v0.9.2 2012-01-18 21:25:03 -05:00
Kenneth Reitz
f3ba022f46 Merge branch 'streaming' of https://github.com/RonnyPfannschmidt/requests into develop 2012-01-18 21:05:56 -05:00
Kenneth Reitz
557480cfe8 SOPA 2012-01-18 11:14:32 -05:00
Kenneth Reitz
5dae0bb9bf Merge pull request #360 from mastahyeti/develop
Make requests.async.send asynchronous
2012-01-17 13:32:04 -08:00
Ben
e38046e6c6 The async.send method wasn't really doing anything useful. It was just calling the r.send() method of the Request object 'r'. It took an optional kwarg pools, and assigned it to r._pools which isn't used by anything. This situation was eaither broken or not fully implemented yet. I modified async.map to call r.send rather than send(r) and then modified async.send to actually make use of the pool that it is passed. 2012-01-17 15:21:09 -06:00
Ben
97be9ee4cd The async.send method wasn't really doing anything useful. It was just calling the r.send() method of the Request object 'r'. It took an optional kwarg pools, and assigned it to r._pools which isn't used by anything. This situation was eaither broken or not fully implemented yet. I modified async.map to call r.send rather than send(r) and then modified async.send to actually make use of the pool that it is passed. 2012-01-17 15:20:52 -06:00
Ben
c658b363e3 The async.send method wasn't really doing anything useful. It was just calling the r.send() method of the Request object 'r'. It took an optional kwarg pools, and assigned it to r._pools which isn't used by anything. This situation was eaither broken or not fully implemented yet. I modified async.map to call r.send rather than send(r) and then modified async.send to actually make use of the pool that it is passed. 2012-01-17 15:17:38 -06:00
Ronny Pfannschmidt
7f9cef0114 close the chunked fd at the end and honor content chunksizes for a potential first chunk 2012-01-17 18:14:47 +01:00
Ronny Pfannschmidt
1c8a141711 remove debug print 2012-01-17 17:39:37 +01:00
Ronny Pfannschmidt
4d38790b5b even more evil, fixes my misstakes with chunked reading 2012-01-17 17:38:52 +01:00
Ronny Pfannschmidt
5cfbb0aaf5 insanity for chunked iteration 2012-01-17 13:16:09 +01:00
Ronny Pfannschmidt
29271a4100 make Response.iter_lines yield the pending buffer if its actually a complete line 2012-01-17 12:47:22 +01:00
Kenneth Reitz
df0dc2b67a +1 2012-01-15 16:39:46 -05:00
Kenneth Reitz
ff286f351c Update AUTHORS.rst 2012-01-13 17:38:28 -05:00
Kenneth Reitz
430125b396 Merge pull request #355 from fkumro/develop
Added prefetch documentation for api
2012-01-13 14:38:09 -08:00
Frank Kumro
0536e5b708 Added prefetch documentation for api 2012-01-12 22:16:31 -05:00
Kenneth Reitz
e6990162e5 Tom Moertel 2012-01-12 12:53:45 -08:00
Kenneth Reitz
d9b304b906 Merge pull request #352 from tmoertel/develop
Fix bug in HTTP-digest auth w/ URI having query string
2012-01-12 12:52:02 -08:00
Tom Moertel
79f5d532c6 Fix bug in HTTP-digest auth w/ URI having query string 2012-01-10 18:18:54 -05:00
Kenneth Reitz
6af6a08b7a Merge pull request #344 from sharat87/patch-1
Do not pass over the `session` argument to `Session.request` method
2012-01-10 11:11:01 -08:00
Kenneth Reitz
96d5044722 Merge pull request #348 from LucianU/develop
Fixed code in 'Event Hooks' section
2012-01-10 11:10:02 -08:00
Kenneth Reitz
4258201900 Update AUTHORS.rst 2012-01-10 11:09:09 -08:00
Kenneth Reitz
11e5126454 Merge pull request #347 from LucianU/patch-1
Printing the entire traceback, instead of just a warning, when an except...
2012-01-10 11:08:53 -08:00
Kenneth Reitz
9e953f3320 Merge pull request #350 from piotr-dobrogost/develop
fixes issue #349
2012-01-10 11:07:45 -08:00
Piotr Dobrogost
bbbeedb0a2 fixes issue #349 2012-01-10 16:47:21 +01:00
Lucian Ursu
de17f4e5a4 Fixed code in 'Event Hooks' section 2012-01-10 12:17:57 +02:00
Lucian Ursu
86eba21b8a Printing the entire traceback, instead of just a warning, when an exception is encountered 2012-01-10 12:10:58 +02:00
Shrikant Sharat Kandula
53d679edba The request method of Session class does not take a session argument. But api.request does. So, it has to be popped before you can send the whole kwargs dict to Session.request method. 2012-01-09 11:16:54 +05:30
Kenneth Reitz
89c90add2c Update docs/index.rst 2012-01-07 18:32:56 -05:00
Kenneth Reitz
a869be1e5a Merge pull request #343 from shazow/urllib3-attribution
urllib3 attribution in docs
2012-01-07 15:28:17 -08:00
Andrey Petrov
68619b8088 Merge branch 'develop' of https://github.com/kennethreitz/requests into urllib3-attribution 2012-01-07 15:19:02 -08:00
Andrey Petrov
534cdd7587 Rewriting history for correctness. 2012-01-07 15:17:14 -08:00
Andrey Petrov
e185aec920 Adding a humble mention of urllib3 to the Requests intros. 2012-01-07 15:17:06 -08:00
Kenneth Reitz
14e8eea0ee Merge remote-tracking branch 'origin/develop' into develop 2012-01-07 17:18:53 -05:00
Kenneth Reitz
2b849545ea urllib3 update 2012-01-07 17:18:49 -05:00
Kenneth Reitz
15396fb2f9 Update docs/user/advanced.rst 2012-01-07 13:44:09 -05:00
Kenneth Reitz
1acef81ed3 Merge pull request #342 from bjedwards/patch-1
Fix syntax error in last commit.
2012-01-06 12:53:56 -08:00
Ben Edwards
a25141f9f4 Fix syntax error in last commit. 2012-01-06 13:52:49 -07:00
Kenneth Reitz
d339ffe9d9 @ojii 2012-01-06 11:36:04 -05:00
Kenneth Reitz
c5ba5888b5 Merge pull request #341 from ojii/patch-1
Give a more useful error message when the URL supplied does not contain ...
2012-01-06 08:20:49 -08:00
Jonas Obrist
3415e6de0b Give a more useful error message when the URL supplied does not contain a schema. 2012-01-06 16:20:33 +01:00
17 changed files with 185 additions and 74 deletions

View File

@@ -34,7 +34,7 @@ Patches and Suggestions
- Alberto Paro
- Jérémy Bethmont
- 潘旭 (Xu Pan)
- Tamás Gulácsi
- Gulácsi Tamás
- Rubén Abad
- Peter Manser
- Jeremy Selier
@@ -69,3 +69,30 @@ Patches and Suggestions
- Ori Livneh
- Jason Emerick
- Bryan Helmig
- Jonas Obrist
- Lucian Ursu
- Tom Moertel
- Frank Kumro Jr
- Mike Waldner
- Luca De Vitis
- digitalxero
- Ronny Pfannschmidt
- Chase Sterling
- Andrey Petrov
- Michael Van Veen
- Devin Sevilla
- Douglas Morrison
- jbergstroem
- jbrendel
- Mark Story
- Nick Hatch
- Pat Nakajima
- Piotr Dobrogost
- Raymond Penners
- Bryan Berg
- Shane McDonald
- Simon Sapin
- Clermont
- Ben Edwards
- verm666
- Juarez Bochi

View File

@@ -1,6 +1,19 @@
History
-------
0.9.2 (2012-01-18)
++++++++++++++++++
* Asyncronous async.send method.
* SSL verify=False bugfix (apparent on windows machines).
* Support for proper chunk streams with boundaries.
* session argument for Session classes.
* Print entire hook tracebacks, not just exception instance.
* Fix response.iter_lines from pending next line.
* Fix but in HTTP-digest auth w/ URI having query strings.
* Fix in Event Hooks section.
* Urllib3 update.
0.9.1 (2012-01-06)
++++++++++++++++++
@@ -87,6 +100,7 @@ History
* OPTION method
* Async pool size throttling
* File uploads send real names
* Vendored in urllib3
0.7.6 (2011-11-07)
++++++++++++++++++

View File

@@ -27,8 +27,9 @@ See `the same code, without Requests <https://gist.github.com/973705>`_.
Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
**PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by httplib, but it does
all the hard work and crazy hacks for you.
response data in the same way. It's powered by httplib and `urllib3
<https://github.com/shazow/urllib3>`_, but it does all the hard work and crazy
hacks for you.
Features

View File

@@ -25,6 +25,14 @@
})();
/* ]]> */
</script>
<script type="text/javascript" src="//www.hellobar.com/hellobar.js"></script>
<script type="text/javascript">
new HelloBar(36402,48802);
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
@@ -54,4 +62,6 @@
s.parentNode.insertBefore(t, s);
})();
</script>
{%- endblock %}

View File

@@ -28,7 +28,7 @@ Things shouldnt be this way. Not in Python.
See `the same code, without Requests <https://gist.github.com/973705>`_.
Requests takes all of the work out of Python HTTP — making your integration with web services seamless. There's no need to manually add query strings to your URLs, or to form-encode your POST data.
Requests takes all of the work out of Python HTTP — making your integration with web services seamless. There's no need to manually add query strings to your URLs, or to form-encode your POST data. Keep-alive and HTTP connection pooling are 100% automatic, powered by `urllib3 <https://github.com/shazow/urllib3>`_, which is embedded within Requests.
Testimonials

View File

@@ -53,9 +53,9 @@ Requests can verify SSL certificates for HTTPS requests, just like a web browser
>>> requests.get('https://kennethreitz.com', verify=True)
requests.exceptions.SSLError: hostname 'kennethreitz.com' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
I don't have SSL setup on this domain, so it fails. Excellent. I do have certs for httpbin.org though::
I don't have SSL setup on this domain, so it fails. Excellent. Github does though::
>>> requests.get('https://httpbin.org', verify=True)
>>> requests.get('https://github.com', verify=True)
<Response [200]>
You can also pass ``verify`` the path to a CA_BUNDLE file for private certs. You can also set the ``REQUESTS_CA_BUNDLE`` environment variable.
@@ -194,7 +194,7 @@ Let's print some request method arguments at runtime::
Let's hijack some arguments this time with a new callback::
def hack_headers(args):
if not args[headers]:
if args.get('headers') is None:
args['headers'] = dict()
args['headers'].update({'X-Testing': 'True'})

View File

@@ -15,8 +15,8 @@ requests
"""
__title__ = 'requests'
__version__ = '0.9.1'
__build__ = 0x000901
__version__ = '0.9.2'
__build__ = 0x000902
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2012 Kenneth Reitz'

View File

@@ -32,9 +32,10 @@ def request(method, url, **kwargs):
:param session: (optional) A :class:`Session` object to be used for the request.
:param config: (optional) A configuration dictionary.
:param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
:param prefetch: (optional) if ``True``, the response content will be immediately downloaded.
"""
s = kwargs.get('session') or sessions.session()
s = kwargs.pop('session') if 'session' in kwargs else sessions.session()
return s.request(method=method, url=url, **kwargs)

View File

@@ -46,15 +46,15 @@ def patched(f):
return wrapped
def send(r, pools=None):
"""Sends a given Request object."""
def send(r, pool=None):
"""Sends the request object using the specified pool. If a pool isn't
specified this method blocks. Pools are useful because you can specify size
and can hence limit concurrency."""
if pools:
r._pools = pools
if pool != None:
return pool.spawn(r.send)
r.send()
return r.response
return gevent.spawn(r.send)
# Patched requests.api functions.
@@ -80,17 +80,13 @@ def map(requests, prefetch=True, size=None):
if size:
pool = Pool(size)
pool.map(send, requests)
pool.join()
jobs = [pool.spawn(r.send) for r in requests]
else:
jobs = [gevent.spawn(send, r) for r in requests]
gevent.joinall(jobs)
jobs = [gevent.spawn(r.send) for r in requests]
gevent.joinall(jobs)
if prefetch:
[r.response.content for r in requests]
return [r.response for r in requests]
return [r.response for r in requests]

View File

@@ -86,7 +86,9 @@ class HTTPDigestAuth(AuthBase):
# XXX not implemented yet
entdig = None
p_parsed = urlparse(r.request.url)
path = p_parsed.path + p_parsed.query
path = p_parsed.path
if p_parsed.query:
path += '?' + p_parsed.query
A1 = '%s:%s:%s' % (self.username, realm, self.password)
A2 = '%s:%s' % (r.request.method, path)

View File

@@ -22,7 +22,7 @@ Available hooks:
"""
import warnings
import traceback
def dispatch_hook(key, hooks, hook_data):
@@ -34,7 +34,7 @@ def dispatch_hook(key, hooks, hook_data):
try:
return hooks.get(key).__call__(hook_data) or hook_data
except Exception, why:
warnings.warn(str(why))
except Exception:
traceback.print_exc()
return hook_data

View File

@@ -82,7 +82,6 @@ class Request(object):
#: Dictionary or byte of querystring data to attach to the
#: :class:`Request <Request>`.
self.params = None
self.params = dict(params or [])
#: True if :class:`Request <Request>` is part of a redirect chain (disables history
#: and HTTPError storage).
@@ -298,7 +297,7 @@ class Request(object):
scheme, netloc, path, params, query, fragment = urlparse(self.url)
if not scheme:
raise ValueError()
raise ValueError("Invalid URL %r: No schema supplied" %self.url)
netloc = netloc.encode('idna')
@@ -612,8 +611,36 @@ class Response(object):
break
yield chunk
self._content_consumed = True
def generate_chunked():
resp = self.raw._original_response
fp = resp.fp
if resp.chunk_left is not None:
pending_bytes = resp.chunk_left
while pending_bytes:
chunk = fp.read(min(chunk_size, pending_bytes))
pending_bytes-=len(chunk)
yield chunk
fp.read(2) # throw away crlf
while 1:
#XXX correct line size? (httplib has 64kb, seems insane)
pending_bytes = fp.readline(40).strip()
pending_bytes = int(pending_bytes, 16)
if pending_bytes == 0:
break
while pending_bytes:
chunk = fp.read(min(chunk_size, pending_bytes))
pending_bytes-=len(chunk)
yield chunk
fp.read(2) # throw away crlf
self._content_consumed = True
fp.close()
gen = generate()
if getattr(getattr(self.raw, '_original_response', None), 'chunked', False):
gen = generate_chunked()
else:
gen = generate()
if 'gzip' in self.headers.get('content-encoding', ''):
gen = stream_decompress(gen, mode='gzip')
@@ -634,7 +661,7 @@ class Response(object):
avoids reading the content at once into memory for large
responses.
"""
#XXX: why rstrip by default
pending = None
for chunk in self.iter_content(chunk_size, decode_unicode=decode_unicode):
if pending is not None:
@@ -643,7 +670,15 @@ class Response(object):
for line in lines[:-1]:
yield line.rstrip()
# Save the last part of the chunk for next iteration, to keep full line together
pending = lines[-1]
# lines may be empty for the last chunk of a chunked response
if lines:
pending = lines[-1]
#if pending is a complete line, give it baack
if pending[-1] == '\n':
yield pending.rstrip()
pending = None
else:
pending = None
# Yield the last line
if pending is not None:

View File

@@ -306,7 +306,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
headers = self.headers
if retries < 0:
raise MaxRetryError("Max retries exceeded for url: %s" % url)
raise MaxRetryError(url)
if timeout is _Default:
timeout = self.timeout
@@ -320,8 +320,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
if self.port:
host = "%s:%d" % (host, self.port)
raise HostChangedError("Connection pool with host '%s' tried to "
"open a foreign host: %s" % (host, url))
raise HostChangedError(host, url, retries - 1)
conn = None
@@ -369,7 +368,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
except (CertificateError), e:
# Name mismatch
raise SSLError(e)
except (HTTPException, SocketError), e:
# Connection broken, discard. It will be replaced next _get_conn().
conn = None
@@ -385,15 +384,12 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
return self.urlopen(method, url, body, headers, retries - 1,
redirect, assert_same_host) # Try again
# Handle redirection
if (redirect and
response.status in [301, 302, 303, 307] and
'location' in response.headers): # Redirect, retry
log.info("Redirecting %s -> %s" %
(url, response.headers.get('location')))
return self.urlopen(method, response.headers.get('location'), body,
headers, retries - 1, redirect,
assert_same_host)
# Handle redirect?
redirect_location = redirect and response.get_redirect_location()
if redirect_location:
log.info("Redirecting %s -> %s" % (url, redirect_location))
return self.urlopen(method, redirect_location, body, headers,
retries - 1, redirect, assert_same_host)
return response

View File

@@ -18,7 +18,9 @@ class SSLError(Exception):
class MaxRetryError(HTTPError):
"Raised when the maximum number of retries is exceeded."
pass
def __init__(self, url):
HTTPError.__init__(self, "Max retries exceeded for url: %s" % url)
self.url = url
class TimeoutError(HTTPError):
@@ -28,7 +30,15 @@ class TimeoutError(HTTPError):
class HostChangedError(HTTPError):
"Raised when an existing pool gets a request for a foreign host."
pass
def __init__(self, original_host, new_url, retries=3):
HTTPError.__init__(self,
"Connection pool with host '%s' tried to open a foreign host: %s" %
(original_host, new_url))
self.original_host = original_host
self.new_url = new_url
self.retries = retries
class EmptyPoolError(HTTPError):
"Raised when a pool runs out of connections and no more are allowed."

View File

@@ -4,20 +4,18 @@
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
import logging
from ._collections import RecentlyUsedContainer
from .connectionpool import (
HTTPConnectionPool, HTTPSConnectionPool,
get_host, connection_from_url,
)
from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
from .connectionpool import get_host, connection_from_url
from .exceptions import HostChangedError
from .request import RequestMethods
__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url']
from .request import RequestMethods
from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
pool_classes_by_scheme = {
'http': HTTPConnectionPool,
'https': HTTPSConnectionPool,
@@ -28,6 +26,8 @@ port_by_scheme = {
'https': 443,
}
log = logging.getLogger(__name__)
class PoolManager(RequestMethods):
"""
@@ -105,7 +105,12 @@ class PoolManager(RequestMethods):
:class:`urllib3.connectionpool.ConnectionPool` can be chosen for it.
"""
conn = self.connection_from_url(url)
return conn.urlopen(method, url, assert_same_host=False, **kw)
try:
return conn.urlopen(method, url, **kw)
except HostChangedError, e:
kw['retries'] = e.retries # Persist retries countdown
return self.urlopen(method, e.new_url, **kw)
class ProxyManager(RequestMethods):

View File

@@ -84,6 +84,19 @@ class HTTPResponse(object):
if preload_content:
self._body = self.read(decode_content=decode_content)
def get_redirect_location(self):
"""
Should we redirect and where to?
:returns: Truthy redirect location string if we got a redirect status
code and valid location. ``None`` if redirect status and no
location. ``False`` if not a redirect status code.
"""
if self.status in [301, 302, 303, 307]:
return self.headers.get('location')
return False
def release_conn(self):
if not self._pool or not self._connection:
return
@@ -98,10 +111,9 @@ class HTTPResponse(object):
return self._body
if self._fp:
return self.read(decode_content=self._decode_content,
cache_content=True)
return self.read(cache_content=True)
def read(self, amt=None, decode_content=True, cache_content=False):
def read(self, amt=None, decode_content=None, cache_content=False):
"""
Similar to :meth:`httplib.HTTPResponse.read`, but with two additional
parameters: ``decode_content`` and ``cache_content``.
@@ -124,6 +136,8 @@ class HTTPResponse(object):
"""
content_encoding = self.headers.get('content-encoding')
decoder = self.CONTENT_DECODERS.get(content_encoding)
if decode_content is None:
decode_content = self._decode_content
data = self._fp and self._fp.read(amt)
@@ -154,8 +168,8 @@ class HTTPResponse(object):
if self._original_response and self._original_response.isclosed():
self.release_conn()
@staticmethod
def from_httplib(r, **response_kw):
@classmethod
def from_httplib(ResponseCls, r, **response_kw):
"""
Given an :class:`httplib.HTTPResponse` instance ``r``, return a
corresponding :class:`urllib3.response.HTTPResponse` object.
@@ -164,14 +178,14 @@ class HTTPResponse(object):
with ``original_response=r``.
"""
return HTTPResponse(body=r,
headers=dict(r.getheaders()),
status=r.status,
version=r.version,
reason=r.reason,
strict=r.strict,
original_response=r,
**response_kw)
return ResponseCls(body=r,
headers=dict(r.getheaders()),
status=r.status,
version=r.version,
reason=r.reason,
strict=r.strict,
original_response=r,
**response_kw)
# Backwards-compatibility methods for httplib.HTTPResponse
def getheaders(self):

View File

@@ -608,9 +608,9 @@ class RequestsTestSuite(unittest.TestCase):
lines = (0, 2, 10, 100)
for i in lines:
r = requests.get(httpbin('stream', str(i)), prefetch=False)
len_lines = len([l for l in r.iter_lines()])
lines = list(r.iter_lines())
len_lines = len(lines)
self.assertEqual(i, len_lines)