Remove the eventles code, this should live in requests.async or something so the user explicitly requests it

Clean up the code to get rid of the unnecessary AuthObject class
Cleanup the send method
Cleanup the convenience methods by adding a request method that they all use.
This commit is contained in:
digitalxero
2011-02-19 10:51:54 -05:00
parent e26570d0d8
commit d1c89333d0
2 changed files with 103 additions and 203 deletions
+96 -184
View File
@@ -13,26 +13,13 @@
from __future__ import absolute_import
import urllib
import urllib2
from urllib2 import HTTPError
try:
import eventlet
eventlet.monkey_patch()
except ImportError:
pass
if not 'eventlet' in locals():
try:
from gevent import monkey
monkey.patch_all()
except ImportError:
pass
from .packages.poster.encode import multipart_encode
from .packages.poster.streaminghttp import register_openers
__all__ = ['Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete', 'add_autoauth', 'AUTOAUTHS',
'RequestException', 'AuthenticationError', 'URLRequired', 'InvalidMethod', 'HTTPError']
__title__ = 'requests'
__version__ = '0.2.4'
__build__ = 0x000204
@@ -43,13 +30,11 @@ __copyright__ = 'Copyright 2011 Kenneth Reitz'
AUTOAUTHS = []
class _Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
setting of HTTP methods.
"""
def __init__(self, url, data=None, headers={}, origin_req_host=None,
unverifiable=False, method=None):
urllib2.Request.__init__(self, url, data, headers, origin_req_host, unverifiable)
@@ -66,61 +51,59 @@ class Request(object):
"""The :class:`Request` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
def __init__(self, url=None, headers=dict(), files=None, method=None,
params=dict(), data=dict(), auth=None, cookiejar=None):
data=dict(), auth=None, cookiejar=None):
self.url = url
self.headers = headers
self.files = files
self.method = method
self.params = params
self.data = data
# url encode data if it's a dict
if isinstance(data, dict):
self.data = urllib.urlencode(data)
else:
self.data = data
self.response = Response()
self.auth = auth
self.cookiejar = cookiejar
self.sent = False
def __repr__(self):
return '<Request [%s]>' % (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."""
if not self.url:
raise URLRequired
def _get_opener(self):
"""Creates appropriate opener object for urllib2."""
_handlers = []
if self.auth or self.cookiejar:
if self.auth:
authr = urllib2.HTTPPasswordMgrWithDefaultRealm()
authr.add_password(None, self.url, self.auth.username, self.auth.password)
authr.add_password(None, self.url, self.auth[0], self.auth[1])
auth_handler = urllib2.HTTPBasicAuthHandler(authr)
_handlers.append(auth_handler)
if self.cookiejar:
cookie_handler = urllib2.HTTPCookieProcessor(cookiejar)
_handlers.append(cookie_handler)
@@ -133,13 +116,13 @@ class Request(object):
def _build_response(self, resp):
"""Build internal Response object from given response."""
self.response.status_code = resp.code
self.response.headers = resp.info().dict
self.response.content = resp.read()
self.response.url = resp.url
def send(self, anyway=False):
"""Sends the request. Returns True of successful, false if not.
If there was an HTTPError during transmission,
@@ -150,106 +133,44 @@ class Request(object):
:param anyway: If True, request will be sent, even if it has
already been sent.
"""
self._checks()
success = False
if self.method in ('GET', 'HEAD', 'DELETE'):
if (not self.sent) or anyway:
req = _Request(("%s?%s" % (self.url, self.data)), method=self.method)
else:
if self.files:
register_openers()
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(self.url, method=self.method)
# url encode GET params if it's a dict
if isinstance(self.params, dict):
params = urllib.urlencode(self.params)
else:
params = self.params
if self.data:
req.data = self.data
req = _Request(("%s?%s" % (self.url, params)), method=self.method)
if self.headers:
req.headers = self.headers
if self.headers:
req.headers = self.headers
if not self.sent or anyway:
try:
opener = self._get_opener()
try:
resp = opener(req)
self._build_response(resp)
self.response.ok = True
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
resp = opener(req)
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
else:
self._build_response(resp)
self.response.ok = True
self.response.cached = False
else:
self.response.cached = True
elif self.method == 'PUT':
if (not self.sent) or anyway:
if self.files:
register_openers()
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method='PUT')
if self.headers:
req.headers.update(self.headers)
else:
req = _Request(self.url, method='PUT')
if self.headers:
req.headers = self.headers
req.data = self.data
try:
opener = self._get_opener()
resp = opener(req)
self._build_response(resp)
self.response.ok = True
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
elif self.method == 'POST':
if (not self.sent) or anyway:
if self.files:
register_openers()
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method='POST')
if self.headers:
req.headers.update(self.headers)
else:
req = _Request(self.url, method='POST')
req.headers = self.headers
# url encode form data if it's a dict
if isinstance(self.data, dict):
req.data = urllib.urlencode(self.data)
else:
req.data = self.data
try:
opener = self._get_opener()
resp = opener(req)
self._build_response(resp)
self.response.ok = True
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
self.sent = self.response.ok
return self.sent
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
@@ -264,36 +185,41 @@ class Response(object):
self.url = None
self.ok = False
self.error = None
self.cached = False
def __repr__(self):
return '<Response [%s]>' % (self.status_code)
def __nonzero__(self):
"""Returns true if status_code is 'OK'."""
return not self.error
def raise_for_status(self):
"""Raises stored HTTPError if one exists."""
if self.error:
raise self.error
class AuthObject(object):
"""The :class:`AuthObject` is a simple HTTP Authentication token. When
given to a Requests function, it enables Basic HTTP Authentication for that
Request. You can also enable Authorization for domain realms with AutoAuth.
See AutoAuth for more details.
:param username: Username to authenticate with.
:param password: Password for given username.
def request(method, url, **kwargs):
"""Sends a `method` 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 of GET/HEAD/DELETE Parameters to send with the :class:`Request`.
:param data: (optional) Bytes/Dictionary of PUT/POST Data to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) 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.
"""
def __init__(self, username, password):
self.username = username
self.password = password
data = kwargs.pop('data', {}) or kwargs.pop('params', {})
r = Request(method=method, url=url, data=data, headers=kwargs.pop('headers', {}),
cookiejar=kwargs.pop('cookies', None), files=kwargs.pop('files', None),
auth=_detect_auth(url, kwargs.pop('auth', None)))
r.send()
return r.response
def get(url, params={}, headers={}, cookies=None, auth=None):
"""Sends a GET request. Returns :class:`Response` object.
@@ -304,12 +230,9 @@ def get(url, params={}, headers={}, cookies=None, auth=None):
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request(method='GET', url=url, params=params, headers=headers,
cookiejar=cookies, auth=_detect_auth(url, auth))
r.send()
return r.response
return request('GET', url, params=params, headers=headers, cookiejar=cookies,
auth=_detect_auth(url, auth))
def head(url, params={}, headers={}, cookies=None, auth=None):
@@ -321,64 +244,53 @@ def head(url, params={}, headers={}, cookies=None, auth=None):
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request(method='HEAD', url=url, params=params, headers=headers,
cookiejar=cookies, auth=_detect_auth(url, auth))
r.send()
return r.response
return request('HEAD', url, params=params, headers=headers, cookiejar=cookies,
auth=_detect_auth(url, auth))
def post(url, data={}, headers={}, files=None, cookies=None, auth=None):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary of POST Data to send with the :class:`Request`.
:param data: (optional) Dictionary of POST data to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request(method='POST', url=url, data=data, headers=headers,
files=files, cookiejar=cookies, auth=_detect_auth(url, auth))
r.send()
return r.response
def put(url, data='', headers={}, files={}, cookies=None, auth=None):
return request('POST', url, data=data, headers=headers, files=files, cookiejar=cookies,
auth=_detect_auth(url, auth))
def put(url, data=b'', headers={}, files={}, cookies=None, auth=None):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Bytes of PUT Data to send with the :class:`Request`.
:param params: (optional) Bytes of PUT Data to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request(method='PUT', url=url, data=data, headers=headers, files=files,
cookiejar=cookies, auth=_detect_auth(url, auth))
r.send()
return r.response
return request('PUT', url, data=data, headers=headers, files=files, cookiejar=cookies,
auth=_detect_auth(url, auth))
def delete(url, params={}, headers={}, cookies=None, auth=None):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of DELETE Parameters to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request(method='DELETE', url=url, params=params, headers=headers,
cookiejar=cookies, auth=_detect_auth(url, auth))
r.send()
return r.response
return request('DELETE', url, params=params, headers=headers, cookiejar=cookies,
auth=_detect_auth(url, auth))
def add_autoauth(url, authobject):
@@ -398,7 +310,7 @@ def add_autoauth(url, authobject):
"""
global AUTOAUTHS
AUTOAUTHS.append((url, authobject))
@@ -409,14 +321,14 @@ def _detect_auth(url, auth):
return _get_autoauth(url) if not auth else auth
def _get_autoauth(url):
"""Returns registered AuthObject for given url if available."""
for (autoauth_url, auth) in AUTOAUTHS:
if autoauth_url in url:
if autoauth_url in url:
return auth
return None
@@ -426,9 +338,9 @@ class RequestException(Exception):
class AuthenticationError(RequestException):
"""The authentication credentials provided were invalid."""
class URLRequired(RequestException):
"""A valid URL is required to make a request."""
class InvalidMethod(RequestException):
"""An inappropriate method was attempted."""
+7 -19
View File
@@ -8,46 +8,40 @@ import requests
class RequestsTestSuite(unittest.TestCase):
"""Requests test cases."""
def setUp(self):
pass
def tearDown(self):
"""Teardown."""
pass
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get('http://google.com')
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_HEAD(self):
r = requests.head('https://google.com')
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTPS_200_OK_GET(self):
auth = requests.AuthObject('requeststest', 'requeststest')
auth = ('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 200)
requests.add_autoauth(url, auth)
r = requests.get(url)
@@ -56,33 +50,27 @@ class RequestsTestSuite(unittest.TestCase):
# reset auto authentication
requests.AUTOAUTHS = []
def test_POSTBIN_GET_POST_FILES(self):
bin = requests.post('http://www.postbin.org/')
bin = requests.get('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
self.assertEqual(post.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')})
self.assertEqual(post2.status_code, 201)
self.assertEqual(post2.status_code, 200)
def test_nonzero_evaluation(self):
r = requests.get('http://google.com/some-404-url')
self.assertEqual(bool(r), False)
r = requests.get('http://google.com/')
self.assertEqual(bool(r), True)
def test_request_ok_set(self):
r = requests.get('http://google.com/some-404-url')
self.assertEqual(r.ok, False)
def test_status_raising(self):
r = requests.get('http://google.com/some-404-url')
self.assertRaises(requests.HTTPError, r.raise_for_status)