Skip to content
This repository was archived by the owner on Dec 16, 2017. It is now read-only.

Commit 578cd04

Browse files
committed
Merge pull request #54 from technoskald/exceptions
No, use local grequests, sigh.
2 parents 2955e07 + abe00de commit 578cd04

1 file changed

Lines changed: 149 additions & 0 deletions

File tree

grequests.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
grequests
5+
~~~~~~~~~
6+
7+
This module contains an asynchronous replica of ``requests.api``, powered
8+
by gevent. All API methods return a ``Request`` instance (as opposed to
9+
``Response``). A list of requests can be sent with ``map()``.
10+
"""
11+
from functools import partial
12+
13+
try:
14+
import gevent
15+
from gevent import monkey as curious_george
16+
from gevent.pool import Pool
17+
except ImportError:
18+
raise RuntimeError('Gevent is required for grequests.')
19+
20+
# Monkey-patch.
21+
curious_george.patch_all(thread=False, select=False)
22+
23+
from requests import Session
24+
25+
26+
__all__ = (
27+
'map', 'imap',
28+
'get', 'options', 'head', 'post', 'put', 'patch', 'delete', 'request'
29+
)
30+
31+
32+
class AsyncRequest(object):
33+
""" Asynchronous request.
34+
35+
Accept same parameters as ``Session.request`` and some additional:
36+
37+
:param session: Session which will do request
38+
:param callback: Callback called on response.
39+
Same as passing ``hooks={'response': callback}``
40+
"""
41+
def __init__(self, method, url, **kwargs):
42+
#: Request method
43+
self.method = method
44+
#: URL to request
45+
self.url = url
46+
#: Associated ``Session``
47+
self.session = kwargs.pop('session', None)
48+
if self.session is None:
49+
self.session = Session()
50+
51+
callback = kwargs.pop('callback', None)
52+
if callback:
53+
kwargs['hooks'] = {'response': callback}
54+
55+
#: The rest arguments for ``Session.request``
56+
self.kwargs = kwargs
57+
#: Resulting ``Response``
58+
self.response = None
59+
60+
def send(self, **kwargs):
61+
"""
62+
Prepares request based on parameter passed to constructor and optional ``kwargs```.
63+
Then sends request and saves response to :attr:`response`
64+
65+
:returns: ``Response``
66+
"""
67+
merged_kwargs = {}
68+
merged_kwargs.update(self.kwargs)
69+
merged_kwargs.update(kwargs)
70+
try:
71+
self.response = self.session.request(self.method,
72+
self.url, **merged_kwargs)
73+
except Exception as e:
74+
self.exception = e
75+
return self
76+
77+
78+
def send(r, pool=None, stream=False):
79+
"""Sends the request object using the specified pool. If a pool isn't
80+
specified this method blocks. Pools are useful because you can specify size
81+
and can hence limit concurrency."""
82+
if pool != None:
83+
return pool.spawn(r.send, stream=stream)
84+
85+
return gevent.spawn(r.send, stream=stream)
86+
87+
88+
# Shortcuts for creating AsyncRequest with appropriate HTTP method
89+
get = partial(AsyncRequest, 'GET')
90+
options = partial(AsyncRequest, 'OPTIONS')
91+
head = partial(AsyncRequest, 'HEAD')
92+
post = partial(AsyncRequest, 'POST')
93+
put = partial(AsyncRequest, 'PUT')
94+
patch = partial(AsyncRequest, 'PATCH')
95+
delete = partial(AsyncRequest, 'DELETE')
96+
97+
# synonym
98+
def request(method, url, **kwargs):
99+
return AsyncRequest(method, url, **kwargs)
100+
101+
102+
def map(requests, stream=False, size=None, exception_handler=None):
103+
"""Concurrently converts a list of Requests to Responses.
104+
105+
:param requests: a collection of Request objects.
106+
:param stream: If True, the content will not be downloaded immediately.
107+
:param size: Specifies the number of requests to make at a time. If None, no throttling occurs.
108+
:param exception_handler: Callback function, called when exception occured. Params: Request, Exception
109+
"""
110+
111+
requests = list(requests)
112+
113+
pool = Pool(size) if size else None
114+
jobs = [send(r, pool, stream=stream) for r in requests]
115+
gevent.joinall(jobs)
116+
117+
ret = []
118+
119+
for request in requests:
120+
if request.response:
121+
ret.append(request.response)
122+
elif exception_handler:
123+
exception_handler(request, request.exception)
124+
125+
return ret
126+
127+
128+
def imap(requests, stream=False, size=2, exception_handler=None):
129+
"""Concurrently converts a generator object of Requests to
130+
a generator of Responses.
131+
132+
:param requests: a generator of Request objects.
133+
:param stream: If True, the content will not be downloaded immediately.
134+
:param size: Specifies the number of requests to make at a time. default is 2
135+
:param exception_handler: Callback function, called when exception occured. Params: Request, Exception
136+
"""
137+
138+
pool = Pool(size)
139+
140+
def send(r):
141+
return r.send(stream=stream)
142+
143+
for request in pool.imap_unordered(send, requests):
144+
if request.response:
145+
yield request.response
146+
elif exception_handler:
147+
exception_handler(request, request.exception)
148+
149+
pool.join()

0 commit comments

Comments
 (0)