Compare commits

...

26 Commits

Author SHA1 Message Date
Kenneth Reitz
fec0d1a9a0 history changes 2011-04-15 17:29:26 -04:00
Kenneth Reitz
642c667566 Merge branch 'release/0.3.2' 2011-04-15 17:27:17 -04:00
Kenneth Reitz
b8b87d416e install simplejson if python < 2.6 2011-04-15 17:27:02 -04:00
Kenneth Reitz
6e14d4704d version bump 2011-04-15 17:26:51 -04:00
Kenneth Reitz
f494cd9c72 add autoauth tuple http basic test 2011-04-15 17:24:03 -04:00
Kenneth Reitz
2814664e91 Fixes #20. 2011-04-15 17:21:38 -04:00
Kenneth Reitz
1359094cc8 test for gzip decompress 2011-04-14 19:46:35 -04:00
Kenneth Reitz
7a62b10ff2 Automatically decompress gzipped responses if content-type is set. fixes #19 2011-04-14 19:44:29 -04:00
Kenneth Reitz
23d5761bd4 accept any extra args to r.read() 2011-04-02 18:22:05 -04:00
Kenneth Reitz
cc16073c36 Merge branch 'develop' 2011-04-01 14:55:24 -04:00
Kenneth Reitz
ed8ff63048 Version bump to 0.3.1 2011-04-01 14:53:53 -04:00
Kenneth Reitz
267a852ba6 Update history 2011-04-01 14:52:33 -04:00
Kenneth Reitz
f2b04f94ca Response.read() [file like object] 2011-03-31 04:47:02 -04:00
Kenneth Reitz
e350bea167 added Request.read 2011-03-31 04:46:19 -04:00
robmadole
2401f14975 Fixing User-agent header problem introduced with Python 2.7.1 2011-03-30 00:07:24 -05:00
Kenneth Reitz
be228043a1 whitespace fix 2011-03-21 20:45:37 -04:00
Kenneth Reitz
48ffb627fe added Aram Dulyan to authors 2011-03-09 13:22:47 -05:00
Aram Dulyan
89192c64f0 An empty cookiejar evaluates to False, so it needs to be compared to None for the cookie functionality to work. 2011-03-09 22:31:13 +11:00
Kenneth Reitz
a67cc5c5a9 someone stole the cookie jar 2011-03-06 19:19:22 -05:00
Kenneth Reitz
fa2f1c5f60 Merge branch 'develop' of https://github.com/robmadole/requests into develop 2011-03-06 19:08:32 -05:00
Kenneth Reitz
1d021c5cc1 Added Rob Madole to AUTHORS 2011-03-06 19:07:29 -05:00
Kenneth Reitz
e17111a5dd testing requests with poster and adding headers 2011-03-06 18:50:28 -05:00
Kenneth Reitz
9fdee250de Better POSTER header compatibility (Fixes #13) 2011-03-06 18:48:44 -05:00
Kenneth Reitz
c6acfef3d0 Added Justin Murphy to AUTHORS 2011-03-06 18:48:14 -05:00
robmadole
e4c690e7bc Initial support for cookies 2011-03-01 09:21:00 -06:00
robmadole
ee43ad2497 Ignore those compiled files 2011-03-01 09:15:14 -06:00
7 changed files with 148 additions and 54 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
MANIFEST
coverage.xml
nosetests.xml
pylint.txt
pylint.txt
*.pyc

View File

@@ -13,4 +13,7 @@ Patches and Suggestions
- Various Pocoo Members
- Chris Adams
- Flavio Percoco Premoli
- Dj Gilcrease
- Dj Gilcrease
- Justin Murphy
- Rob Madole
- Aram Dulyan

View File

@@ -1,6 +1,21 @@
History
-------
0.3.2 (2011-04-15)
++++++++++++++++++
* Automatic Decompression of GZip Encoded Content
* AutoAuth Support for Tupled HTTP Auth
0.3.1 (2011-04-01)
++++++++++++++++++
* Cookie Changes
* Response.read()
* Poster fix
0.3.0 (2011-02-25)
++++++++++++++++++

View File

@@ -48,9 +48,9 @@ copyright = u'2011, Kenneth Reitz'
# built documents.
#
# The short X.Y version.
version = '0.2.0'
version = '0.3.2'
# The full version, including alpha/beta/rc tags.
release = '0.2.0'
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -14,6 +14,7 @@ from __future__ import absolute_import
import urllib
import urllib2
import zlib
from urllib2 import HTTPError
from urlparse import urlparse
@@ -24,15 +25,15 @@ from .packages.poster.streaminghttp import register_openers, get_handlers
__title__ = 'requests'
__version__ = '0.3.0'
__build__ = 0x000300
__version__ = '0.3.2'
__build__ = 0x000302
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
__all__ = [
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
'URLRequired', 'InvalidMethod', 'HTTPError'
]
@@ -63,7 +64,7 @@ class Request(object):
def __init__(self, url=None, headers=dict(), files=None, method=None,
data=dict(), auth=None, cookiejar=None):
self.url = url
self.headers = headers
self.files = files
@@ -111,6 +112,9 @@ class Request(object):
_handlers = []
if self.cookiejar is not None:
_handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar))
if self.auth:
if not isinstance(self.auth.handler, (urllib2.AbstractBasicAuthHandler, urllib2.AbstractDigestAuthHandler)):
auth_manager.add_password(self.auth.realm, self.url, self.auth.username, self.auth.password)
@@ -119,26 +123,42 @@ class Request(object):
_handlers.append(self.auth.handler)
_handlers.extend(get_handlers())
opener = urllib2.build_opener(*_handlers)
return opener.open
else:
if not _handlers:
return urllib2.urlopen
_handlers.extend(get_handlers())
opener = urllib2.build_opener(*_handlers)
if self.headers:
# Allow default headers in the opener to be overloaded
normal_keys = [k.capitalize() for k in self.headers]
for key, val in opener.addheaders[:]:
if key not in normal_keys:
continue
# Remove it, we have a value to take its place
opener.addheaders.remove((key, val))
return opener.open
def _build_response(self, resp):
"""Build internal Response object from given response."""
self.response.status_code = getattr(resp, 'code', None)
self.response.headers = getattr(resp.info(), 'dict', None)
self.response.url = getattr(resp, 'url', None)
self.response.content = resp.read()
if self.response.headers.get('content-encoding', None) == 'gzip':
try:
self.response.content = zlib.decompress(self.response.content, 16+zlib.MAX_WBITS)
except zlib.error:
pass
self.response.url = getattr(resp, 'url', None)
@staticmethod
def _build_url(url, data):
"""Build URLs."""
if urlparse(url).query:
return '%s&%s' % (url, data)
else:
@@ -147,7 +167,6 @@ class Request(object):
else:
return url
def send(self, anyway=False):
"""Sends the request. Returns True of successful, false if not.
If there was an HTTPError during transmission,
@@ -170,20 +189,24 @@ class Request(object):
if self.data:
self.files.update(self.data)
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(self.url, data=self._enc_data, method=self.method)
if self.headers:
req.headers = self.headers
req.headers.update(self.headers)
if not self.sent or anyway:
try:
opener = self._get_opener()
resp = opener(req)
resp = opener(req)
if self.cookiejar is not None:
self.cookiejar.extract_cookies(resp, req)
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
@@ -195,13 +218,14 @@ class Request(object):
else:
self.response.cached = True
self.sent = self.response.ok
return self.sent
def read(self, *args):
return self.response.read()
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
:class:`Request.response <response>` attribute, which is an instance of
@@ -232,11 +256,13 @@ class Response(object):
if self.error:
raise self.error
def read(self, *args):
return self.content
class AuthManager(object):
"""Authentication Manager."""
def __new__(cls):
singleton = cls.__dict__.get('__singleton__')
if singleton is not None:
@@ -258,8 +284,16 @@ class AuthManager(object):
def add_auth(self, uri, auth):
"""Registers AuthObject to AuthManager."""
uri = self.reduce_uri(uri, False)
# try to make it an AuthObject
if not isinstance(auth, AuthObject):
try:
auth = AuthObject(*auth)
except TypeError:
pass
self._auth[uri] = auth
def add_password(self, realm, uri, user, passwd):
@@ -267,9 +301,9 @@ class AuthManager(object):
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
reduced_uri = tuple([self.reduce_uri(u, False) for u in uri])
if reduced_uri not in self.passwd:
self.passwd[reduced_uri] = {}
self.passwd[reduced_uri] = (user, passwd)
@@ -286,8 +320,14 @@ class AuthManager(object):
def get_auth(self, uri):
uri = self.reduce_uri(uri, False)
return self._auth.get(uri, None)
(in_domain, in_path) = self.reduce_uri(uri, False)
for domain, path, authority in (
(i[0][0], i[0][1], i[1]) for i in self._auth.iteritems()
):
if in_domain == domain:
if path in in_path:
return authority
def reduce_uri(self, uri, default_port=True):
@@ -311,9 +351,10 @@ class AuthManager(object):
}.get(scheme)
if dport is not None:
authority = "%s:%d" % (host, dport)
return authority, path
def is_suburi(self, base, test):
"""Check if test is below base in a URI tree
@@ -422,8 +463,8 @@ 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.
"""
return request('GET', url, params=params, headers=headers, cookiejar=cookies, auth=auth)
return request('GET', url, params=params, headers=headers, cookies=cookies, auth=auth)
def head(url, params={}, headers={}, cookies=None, auth=None):
@@ -436,7 +477,7 @@ def head(url, params={}, headers={}, cookies=None, auth=None):
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
return request('HEAD', url, params=params, headers=headers, cookiejar=cookies, auth=auth)
return request('HEAD', url, params=params, headers=headers, cookies=cookies, auth=auth)
def post(url, data={}, headers={}, files=None, cookies=None, auth=None):
@@ -450,7 +491,7 @@ def post(url, data={}, headers={}, files=None, cookies=None, auth=None):
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
return request('POST', url, data=data, headers=headers, files=files, cookiejar=cookies, auth=auth)
return request('POST', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth)
def put(url, data='', headers={}, files={}, cookies=None, auth=None):
@@ -464,7 +505,7 @@ def put(url, data='', headers={}, files={}, cookies=None, auth=None):
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
return request('PUT', url, data=data, headers=headers, files=files, cookiejar=cookies, auth=auth)
return request('PUT', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth)
def delete(url, params={}, headers={}, cookies=None, auth=None):
@@ -477,7 +518,7 @@ def delete(url, params={}, headers={}, cookies=None, auth=None):
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
return request('DELETE', url, params=params, headers=headers, cookiejar=cookies, auth=auth)
return request('DELETE', url, params=params, headers=headers, cookies=cookies, auth=auth)

View File

@@ -8,7 +8,7 @@ import requests
from distutils.core import setup
if sys.argv[-1] == "publish":
os.system("python setup.py sdist upload")
sys.exit()
@@ -16,10 +16,11 @@ if sys.argv[-1] == "publish":
if sys.argv[-1] == "test":
os.system("python test_requests.py")
sys.exit()
required = []
# if python > 2.6, require simplejson
if sys.version_info[:2] < (2,6):
required.append('simplejson')
setup(
name='requests',

63
test_requests.py Normal file → Executable file
View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import unittest
import cookielib
import requests
@@ -28,20 +29,25 @@ class RequestsTestSuite(unittest.TestCase):
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://www.google.com/search', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://google.com/search?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_user_agent_transfers(self):
"""Issue XX"""
heads = {'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'}
r = requests.get('http://whatsmyua.com', headers=heads);
self.assertTrue(heads['User-agent'] in r.content)
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
self.assertEqual(r.status_code, 200)
@@ -64,11 +70,9 @@ class RequestsTestSuite(unittest.TestCase):
requests.auth_manager.empty()
def test_POSTBIN_GET_POST_FILES(self):
bin = requests.post('http://www.postbin.org/')
print bin.url
self.assertEqual(bin.status_code, 200)
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
@@ -76,35 +80,64 @@ class RequestsTestSuite(unittest.TestCase):
self.assertEqual(post2.status_code, 201)
def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')}, data={'some': 'data'})
self.assertEqual(post2.status_code, 201)
def test_POSTBIN_GET_POST_FILES_WITH_HEADERS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')},
headers={'User-Agent': 'requests-tests'})
self.assertEqual(post2.status_code, 201)
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)
r = requests.get('http://google.com/')
self.assertFalse(r.error)
r.raise_for_status()
def test_cookie_jar(self):
"""
.. todo:: This really doesn't test to make sure the cookie is working
"""
jar = cookielib.CookieJar()
self.assertFalse(jar)
requests.get('http://google.com', cookies=jar)
self.assertTrue(jar)
def test_decompress_gzip(self):
r = requests.get('http://api.stackoverflow.com/1.1/users/495995/top-answer-tags')
r.content.decode('ascii')
def test_autoauth(self):
conv_auth = ('requeststest', 'requeststest')
requests.auth_manager.add_auth('convore.com', conv_auth)
r = requests.get('https://convore.com/api/account/verify.json')
self.assertEquals(r.status_code, 200)
if __name__ == '__main__':
unittest.main()