Compare commits

..

105 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
Kenneth Reitz
044252ea7b Merge branch 'release/0.3.0' 2011-02-25 09:00:23 -05:00
Kenneth Reitz
f8032e08ae tests cleanups 2011-02-25 08:58:24 -05:00
Kenneth Reitz
f0d2ac3dd5 Readme update 2011-02-25 08:58:17 -05:00
Kenneth Reitz
270898e521 Readme Updates 2011-02-25 08:48:09 -05:00
Kenneth Reitz
897dd3aaa8 whitespace 2011-02-25 08:11:17 -05:00
Kenneth Reitz
dd01f8661e hacking whitespace fixes 2011-02-25 08:11:10 -05:00
Kenneth Reitz
99c3970fd3 history 2011-02-25 08:04:03 -05:00
Kenneth Reitz
839c8cf150 postbin tests were broken 2011-02-25 08:00:23 -05:00
Kenneth Reitz
b252cd8403 allow files to be upload along with form data 2011-02-25 08:00:11 -05:00
Kenneth Reitz
b3b2b19212 hmmm 2011-02-25 07:39:19 -05:00
Kenneth Reitz
a74e954788 test improvements 2011-02-25 07:21:11 -05:00
Kenneth Reitz
32f75585ad url builder, cleanups 2011-02-25 07:20:59 -05:00
Kenneth Reitz
9a8b25d5fa makin' history 2011-02-25 07:20:37 -05:00
Kenneth Reitz
14ba99f975 prettier imports 2011-02-25 05:03:12 -05:00
Kenneth Reitz
ccb7fdd8c2 Version bump 2011-02-25 04:48:10 -05:00
Kenneth Reitz
9a1d90d184 Proper async module docs 2011-02-25 04:43:18 -05:00
Kenneth Reitz
22798ca32b Prepping for v0.3.0 release. 2011-02-25 04:40:33 -05:00
Kenneth Reitz
3fe4753d5d Cleanups 2011-02-24 02:06:48 -05:00
Kenneth Reitz
d91157c567 Merge branch 'develop' into merge/xero 2011-02-24 01:53:55 -05:00
Kenneth Reitz
e7b8e9371a merge changes 2011-02-24 01:51:53 -05:00
Kenneth Reitz
1f60e1004c Merge https://github.com/Digitalxero/requests into merge/xero 2011-02-24 01:45:44 -05:00
Kenneth Reitz
9e45d201f3 Added Dj Gilcrease to AUTHORS. 2011-02-24 01:38:58 -05:00
Kenneth Reitz
47ecb3662d !+= 2011-02-24 01:30:00 -05:00
Kenneth Reitz
7369e47f18 Added Flavio to AUTHORS 2011-02-24 01:29:51 -05:00
Flavio Percoco Premoli
6f5b64bca6 small workaround for issue #7 2011-02-23 08:49:08 +01:00
Kenneth Reitz
e788ca7ee6 Safer error response handling. 2011-02-21 14:36:41 -05:00
Kenneth Reitz
68dea921e7 Added HACKING file. 2011-02-21 02:25:01 -05:00
digitalxero
f96157d259 Fix a syntax error on py2.5 and pypy 2011-02-19 15:33:24 -05:00
digitalxero
718f45698d Reset copyright on the async module 2011-02-19 15:21:47 -05:00
digitalxero
15761fc126 Made it so people can add their own auth handlers and not just the 4 provided by Python 2011-02-19 15:07:58 -05:00
digitalxero
edfdef2396 Add in a proper AuthManager instead of the list version that was being used.
Added support for all Auth types that python supports
2011-02-19 15:04:56 -05:00
digitalxero
a9e18e9106 Update the README 2011-02-19 11:18:29 -05:00
digitalxero
3b82b06adc Added an async module to make it explicit to the user that they are using async calls. 2011-02-19 11:13:10 -05:00
digitalxero
d1c89333d0 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.
2011-02-19 10:51:54 -05:00
Kenneth Reitz
e26570d0d8 Merge branch 'hotfix/date' into develop 2011-02-19 01:30:22 -05:00
Kenneth Reitz
8b3c4da2c7 Merge branch 'hotfix/date' 2011-02-19 01:30:10 -05:00
Kenneth Reitz
5c4aa1f4b7 history date fix 2011-02-19 01:30:03 -05:00
Kenneth Reitz
d2cdefa7df Merge branch 'release/0.2.4' 2011-02-19 01:03:39 -05:00
Kenneth Reitz
720d0b2c38 Version needs to be available externally. 2011-02-19 01:02:13 -05:00
Kenneth Reitz
d2584ff853 Production Ready! Added Python 2.5 to Support list. 2011-02-19 01:02:01 -05:00
Kenneth Reitz
433452f9f1 Simpler setup.py versioning. 2011-02-19 01:01:51 -05:00
Kenneth Reitz
a34e0bea8b Version Bump (v0.2.4) 2011-02-19 01:00:03 -05:00
Kenneth Reitz
f66b35454f v0.2.4 Release information 2011-02-19 00:59:56 -05:00
Kenneth Reitz
b5c2806e8c Roadmap update 2011-02-19 00:43:27 -05:00
Kenneth Reitz
5072399f7a Import fix for python2.5 2011-02-19 00:41:28 -05:00
Kenneth Reitz
736cb70e9a Merge branch 'develop' of github.com:kennethreitz/requests into develop 2011-02-19 00:38:09 -05:00
Kenneth Reitz
8eafb8d3df Added autoauth tests 2011-02-19 00:37:33 -05:00
Kenneth Reitz
e8234a0137 import fix 2011-02-18 16:13:49 -05:00
Kenneth Reitz
fbe0393eb3 cstring io fix for py3 2011-02-18 16:08:26 -05:00
Kenneth Reitz
b82e72eb04 cStringIO fix. 2011-02-18 16:00:34 -05:00
Kenneth Reitz
a7c87edebe screw 2.4 2011-02-18 15:57:50 -05:00
Kenneth Reitz
7e20f32b02 pypy configuration 2011-02-18 04:41:42 -05:00
Kenneth Reitz
4419e7183f no more test suite 2011-02-18 04:41:34 -05:00
Kenneth Reitz
6bde955170 Python 2.5 Fixes. 2011-02-18 04:39:53 -05:00
Kenneth Reitz
952970f403 Merge branch 'release/0.9.4' 2011-02-18 03:35:18 -05:00
Kenneth Reitz
d3911da653 tox update (usable now?) 2011-02-18 03:32:10 -05:00
Kenneth Reitz
7b6ed48a86 tox setup for 2.4-2.7 2011-02-17 19:38:40 -05:00
Kenneth Reitz
7229dbe033 Making everyone else happy. 2011-02-17 16:22:03 -05:00
Kenneth Reitz
015d6b78d5 cleaning up line lengths 2011-02-17 16:20:53 -05:00
Kenneth Reitz
f4c4bf3d0b Much nicer helper functions and constructors 2011-02-17 16:17:46 -05:00
Kenneth Reitz
efedbd9775 added test_suite.sh for CI 2011-02-17 13:14:03 -05:00
Kenneth Reitz
f4e03a5d32 gitignore update for jenkins 2011-02-17 13:13:52 -05:00
Kenneth Reitz
34ca692c9c from __future__ import absolute_imports 2011-02-16 12:47:58 -05:00
Kenneth Reitz
a5517960ea history indent fix for rest/pypi 2011-02-15 10:02:39 -05:00
Kenneth Reitz
fa42c12e5b I need autodoc. 2011-02-15 09:58:02 -05:00
Kenneth Reitz
8ac29b40e9 new documentation on Request changes. 2011-02-15 09:51:46 -05:00
Kenneth Reitz
adb53c2b4f Merge branch 'release/0.2.3' into develop 2011-02-15 09:46:58 -05:00
Kenneth Reitz
53e785f656 Merge branch 'release/0.2.3' 2011-02-15 09:46:55 -05:00
Kenneth Reitz
d5256802d4 v0.2.3 2011-02-15 09:46:46 -05:00
Kenneth Reitz
933cf49daa v0.2.3 2011-02-15 09:45:54 -05:00
Kenneth Reitz
83a9bcf1a1 Updating AUTHORS 2011-02-15 09:44:52 -05:00
Kenneth Reitz
4ad73c3655 leave room for _ 2011-02-15 09:42:05 -05:00
Kenneth Reitz
7ad08c5eda New tests for new HTTP STATUS Handling 2011-02-15 09:37:21 -05:00
Kenneth Reitz
4f1301f6ad Now Requests has it's own HTTPError 2011-02-15 09:36:34 -05:00
Kenneth Reitz
6d8a531e87 HTTPError changes are going down in history 2011-02-15 09:26:44 -05:00
Kenneth Reitz
748633d616 raise_for_status is better 2011-02-15 09:26:07 -05:00
Kenneth Reitz
ce122cc811 None, not False 2011-02-15 09:22:11 -05:00
Kenneth Reitz
c900b4379b Added new HTTPError handing mechanisms. 2011-02-15 09:18:43 -05:00
Kenneth Reitz
d6fa43fba7 Merge branch 'release/0.2.2' into develop 2011-02-14 12:59:22 -05:00
15 changed files with 752 additions and 444 deletions

6
.gitignore vendored
View File

@@ -1,2 +1,6 @@
.idea
MANIFEST
MANIFEST
coverage.xml
nosetests.xml
pylint.txt
*.pyc

View File

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

15
HACKING Normal file
View File

@@ -0,0 +1,15 @@
Where possible, please follow PEP8 with regard to coding style. Sometimes the
line length restriction is too hard to follow, so don't bend over backwards
there.
Triple-quotes should always be """, single quotes are ' unless using " would
result in less escaping within the string.
All modules, functions, and methods should be well documented reStructuredText
for Sphinx AutoDoc.
All functionality should be available in pure Python. Optional C (via Cython)
implementations may be written for performance reasons, but should never
replace the Python implementation.
Lastly, don't take yourself too seriously :)

View File

@@ -1,6 +1,51 @@
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)
++++++++++++++++++
* Automatic Authentication API Change
* Smarter Query URL Parameterization
* Allow file uploads and POST data together
* New Authentication Manager System
- Simpler Basic HTTP System
- Supports all build-in urllib2 Auths
- Allows for custom Auth Handlers
0.2.4 (2011-02-19)
++++++++++++++++++
* Python 2.5 Support
* PyPy-c v1.4 Support
* Auto-Authentication tests
* Improved Request object constructor
0.2.3 (2011-02-15)
++++++++++++++++++
* New HTTPHandling Methods
- Reponse.__nonzero__ (false if bad HTTP Status)
- Response.ok (True if expected HTTP Status)
- Response.error (Logged HTTPError if bad HTTP Status)
- Reponse.raise_for_status() (Raises stored HTTPError)
0.2.2 (2011-02-14)
++++++++++++++++++

View File

@@ -1,11 +1,11 @@
Requests: The Simple (e.g. usable) HTTP Module
==============================================
Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging).
Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging).
But this one's different. This one's going to be awesome. And simple.
Really simple.
Really simple.
Features
--------
@@ -15,8 +15,8 @@ Features
+ Simple Data/Params Request Attachment
+ Simple Multipart File Uploads
+ CookieJar Support
- Simple Basic HTTP Authentication
- Simple Authentication
+ Simple URL + HTTP Auth Registry
@@ -30,23 +30,23 @@ It couldn't be simpler. ::
HTTPS? Basic Authentication? ::
>>> r = requests.get('https://convore.com/api/account/verify.json')
>>> r.status_code
401
Uh oh, we're not authorized! Let's add authentication. ::
>>> conv_auth = requests.AuthObject('requeststest', 'requeststest')
>>> conv_auth = ('requeststest', 'requeststest')
>>> r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
>>> r.status_code
200
200
>>> r.headers['content-type']
'application/json'
>>> r.content
'{"username": "requeststest", "url": "/users/requeststest/", "id": "9408", "img": "censored-long-url"}'
@@ -54,55 +54,67 @@ Uh oh, we're not authorized! Let's add authentication. ::
API
---
**Requests:**
All request functions return a Response object (see below).
If a {filename: fileobject} dictionary is passed in (files=...), a multipart_encode upload will be performed.
If CookieJar object is is passed in (cookies=...), the cookies will be sent with the request.
GET Requests
>>> request.get(url, params={}, headers={}, cookies=None, auth=None)
<request object>
HEAD Requests
>>> request.head(url, params={}, headers={}, cookies=None, auth=None)
<request object>
PUT Requests
>>> request.put(url, data='', headers={}, files={}, cookies=None, auth=None)
<request object>
POST Requests
>>> request.post(url, data={}, headers={}, files={}, cookies=None, auth=None)
<request object>
DELETE Requests
>>> request.delete(url, params={}, headers={}, cookies=None, auth=None)
<request object>
**Responses:**
Request.status_code:
Response.status_code
(Integer) Received HTTP Status Code Response
Request.headers:
Response.headers
(Dictionary) Received HTTP Response Headers
Request.content:
Response.content
(Bytes) Received Content
Request.url
(String) URL of response. Useful for detecting redirects.
Response.url
(String) URL of response. Useful for detecting redirects.
Response.ok
(Bool) True if no errors occurred during the request, and the status_code is kosher.
Response.cached
(Bool) True if Response.content is stored within the object.
Response.error
(HTTPError) If an HTTPError occurred (e.g. status of 404), Otherwise this is None.
Response.raise_for_status()
Raises HTTPError if a request is not kosher.
**HTTP Authentication Registry:**
You can register AuthObjects to automatically enable HTTP Authentication on requests that contain a registered base URL string.
>>> requests.add_autoauth(url, authobject)
>>> requests.auth_manager.add_auth(url, authobject)
@@ -112,13 +124,13 @@ Installation
To install requests, simply: ::
$ pip install requests
Or, if you absolutely must: ::
$ easy_install requests
But, you really shouldn't do that.
Contribute
@@ -131,8 +143,10 @@ If you'd like to contribute, simply fork `the repository`_, commit your changes
Roadmap
-------
- Sphinx Documentation
- Exhaustive Unittests
- Sphinx Documentation (http://code.kennethreitz.com/requests/)
- Exhaustive unit tests
- Get rid of Poster (gets really nasty in py3.x)
- Python 3.x Support
.. _`the repository`: http://github.com/kennethreitz/requests
.. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS

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

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from . import packages
from .core import *
import packages
from core import *
from core import __version__

48
requests/async.py Normal file
View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
"""
requests.async
~~~~~~~~~~~~~~
This module implements the main Requests system, after monkey-patching
the urllib2 module with eventlet or gevent..
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
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
if not 'eventlet' in locals():
raise ImportError('No Async adaptations of urllib2 found!')
from .core import *
__all__ = [
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
'URLRequired', 'InvalidMethod', 'HTTPError'
]

View File

@@ -10,134 +10,165 @@
:license: ISC, see LICENSE for more details.
"""
from __future__ import absolute_import
import urllib
import urllib2
import zlib
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 urllib2 import HTTPError
from urlparse import urlparse
from .packages.poster.encode import multipart_encode
from .packages.poster.streaminghttp import register_openers
from .packages.poster.streaminghttp import register_openers, get_handlers
__title__ = 'requests'
__version__ = '0.2.2'
__build__ = 0x000202
__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',
'URLRequired', 'InvalidMethod', 'HTTPError'
]
AUTOAUTHS = []
class _Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
"""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)
self.method = method
def get_method(self):
if self.method:
return self.method
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)
self.method = method
return urllib2.Request.get_method(self)
def get_method(self):
if self.method:
return self.method
return urllib2.Request.get_method(self)
class Request(object):
"""The :class:`Request` object. It carries out all functionality of
"""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):
self.url = None
self.headers = dict()
self.files = None
self.method = None
self.params = {}
self.data = {}
self.response = Response()
self.auth = None
self.cookiejar = None
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
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
def _get_opener(self):
"""Creates appropriate opener object for urllib2."""
def __init__(self, url=None, headers=dict(), files=None, method=None,
data=dict(), auth=None, cookiejar=None):
_handlers = []
self.url = url
self.headers = headers
self.files = files
self.method = method
self.data = data
if self.auth or self.cookiejar:
# url encode data if it's a dict
if hasattr(data, 'items'):
self._enc_data = urllib.urlencode(data)
else:
self._enc_data = data
if self.auth:
self.response = Response()
authr = urllib2.HTTPPasswordMgrWithDefaultRealm()
authr.add_password(None, self.url, self.auth.username, self.auth.password)
auth_handler = urllib2.HTTPBasicAuthHandler(authr)
_handlers.append(auth_handler)
if self.cookiejar:
cookie_handler = urllib2.HTTPCookieProcessor(cookiejar)
_handlers.append(cookie_handler)
opener = urllib2.build_opener(*_handlers)
return opener.open
else:
return urllib2.urlopen
if isinstance(auth, (list, tuple)):
auth = AuthObject(*auth)
if not auth:
auth = auth_manager.get_auth(self.url)
self.auth = auth
self.cookiejar = cookiejar
self.sent = False
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 __repr__(self):
return '<Request [%s]>' % (self.method)
def send(self, anyway=False):
"""Sends the request. Returns True of successful, false if not.
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.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)
self.auth.handler = self.auth.handler(auth_manager)
auth_manager.add_auth(self.url, self.auth)
_handlers.append(self.auth.handler)
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.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:
if data:
return '%s?%s' % (url, data)
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,
self.response.status_code will contain the HTTPError code.
@@ -146,145 +177,285 @@ class Request(object):
:param anyway: If True, request will be sent, even if it has
already been sent.
"""
self._checks()
success = False
self._checks()
if self.method in ('GET', 'HEAD', 'DELETE'):
req = _Request(self._build_url(self.url, self._enc_data), method=self.method)
else:
success = False
if self.files:
register_openers()
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.update(self.headers)
if not self.sent or anyway:
try:
opener = self._get_opener()
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
else:
self._build_response(resp)
self.response.ok = True
self.response.cached = False
else:
self.response.cached = True
self.sent = self.response.ok
return self.sent
if self.method in ('GET', 'HEAD', 'DELETE'):
if (not self.sent) or anyway:
# url encode GET params if it's a dict
if isinstance(self.params, dict):
params = urllib.urlencode(self.params)
else:
params = self.params
req = _Request(("%s?%s" % (self.url, params)), method=self.method)
if self.headers:
req.headers = self.headers
opener = self._get_opener()
try:
resp = opener(req)
self._build_response(resp)
success = True
except urllib2.HTTPError as why:
self._build_response(why)
success = False
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)
success = True
except urllib2.HTTPError as why:
self._build_response(why)
success = False
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)
success = True
except urllib2.HTTPError as why:
self._build_response(why)
success = False
self.sent = True if success else False
return success
def read(self, *args):
return self.response.read()
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
"""The :class:`Request` object. All :class:`Request` objects contain a
:class:`Request.response <response>` attribute, which is an instance of
this class.
"""
def __init__(self):
self.content = None
self.status_code = None
self.headers = dict()
self.url = None
def __repr__(self):
return '<Response [%s]>' % (self.status_code)
def __init__(self):
self.content = None
self.status_code = None
self.headers = dict()
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
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:
return singleton
cls.__singleton__ = singleton = object.__new__(cls)
return singleton
def __init__(self):
self.passwd = {}
self._auth = {}
def __repr__(self):
return '<AuthManager [%s]>' % (self.method)
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):
"""Adds password to AuthManager."""
# 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)
def find_user_password(self, realm, authuri):
for uris, authinfo in self.passwd.iteritems():
reduced_authuri = self.reduce_uri(authuri, False)
for uri in uris:
if self.is_suburi(uri, reduced_authuri):
return authinfo
return (None, None)
def get_auth(self, uri):
(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):
"""Accept authority or URI and extract only the authority and path."""
# note HTTP URLs do not have a userinfo component
parts = urllib2.urlparse.urlsplit(uri)
if parts[1]:
# URI
scheme = parts[0]
authority = parts[1]
path = parts[2] or '/'
else:
# host or host:port
scheme = None
authority = uri
path = '/'
host, port = urllib2.splitport(authority)
if default_port and port is None and scheme is not None:
dport = {"http": 80,
"https": 443,
}.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
Both args must be URIs in reduced form.
"""
if base == test:
return True
if base[0] != test[0]:
return False
common = urllib2.posixpath.commonprefix((base[1], test[1]))
if len(common) == len(base[1]):
return True
return False
def empty(self):
self.passwd = {}
def remove(self, uri, realm=None):
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
for default_port in True, False:
reduced_uri = tuple([self.reduce_uri(u, default_port) for u in uri])
del self.passwd[reduced_uri][realm]
def __contains__(self, uri):
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
uri = tuple([self.reduce_uri(u, False) for u in uri])
if uri in self.passwd:
return True
return False
auth_manager = AuthManager()
class AuthObject(object):
"""The :class:`AuthObject` is a simple HTTP Authentication token. When
"""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.
:param realm: (optional) the realm this auth applies to
:param handler: (optional) basic || digest || proxy_basic || proxy_digest
"""
def __init__(self, username, password):
self.username = username
self.password = password
_handlers = {
'basic': urllib2.HTTPBasicAuthHandler,
'digest': urllib2.HTTPDigestAuthHandler,
'proxy_basic': urllib2.ProxyBasicAuthHandler,
'proxy_digest': urllib2.ProxyDigestAuthHandler
}
def __init__(self, username, password, handler='basic', realm=None):
self.username = username
self.password = password
self.realm = realm
if isinstance(handler, basestring):
self.handler = self._handlers.get(handler.lower(), urllib2.HTTPBasicAuthHandler)
else:
self.handler = handler
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.
"""
data = kwargs.pop('data', dict()) or kwargs.pop('params', dict())
r = Request(method=method, url=url, data=data, headers=kwargs.pop('headers', {}),
cookiejar=kwargs.pop('cookies', None), files=kwargs.pop('files', None),
auth=kwargs.pop('auth', auth_manager.get_auth(url)))
r.send()
return r.response
def get(url, params={}, headers={}, cookies=None, auth=None):
"""Sends a GET request. Returns :class:`Response` object.
"""Sends a GET 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`.
@@ -292,23 +463,12 @@ 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()
r.method = 'GET'
r.url = url
r.params = params
r.headers = headers
r.cookiejar = cookies
r.auth = _detect_auth(url, auth)
r.send()
return r.response
return request('GET', url, params=params, headers=headers, cookies=cookies, auth=auth)
def head(url, params={}, headers={}, cookies=None, auth=None):
"""Sends a HEAD request. Returns :class:`Response` object.
"""Sends a HEAD 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`.
@@ -316,148 +476,61 @@ 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()
r.method = 'HEAD'
r.url = url
# return response object
r.params = params
r.headers = headers
r.cookiejar = cookies
r.auth = _detect_auth(url, auth)
r.send()
return r.response
return request('HEAD', url, params=params, headers=headers, cookies=cookies, auth=auth)
def post(url, data={}, headers={}, files=None, cookies=None, auth=None):
"""Sends a POST request. Returns :class:`Response` object.
"""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()
r.url = url
r.method = 'POST'
r.data = data
if files:
r.files = files
r.headers = headers
r.cookiejar = cookies
r.auth = _detect_auth(url, auth)
r.send()
return r.response
return request('POST', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth)
def put(url, data='', headers={}, files={}, cookies=None, auth=None):
"""Sends a PUT request. Returns :class:`Response` object.
"""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()
r.url = url
r.method = 'PUT'
r.data = data
r.files = files
r.headers = headers
r.cookiejar = cookies
r.auth = _detect_auth(url, auth)
r.send()
return r.response
return request('PUT', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth)
def delete(url, params={}, headers={}, cookies=None, auth=None):
"""Sends a DELETE request. Returns :class:`Response` object.
"""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()
r.url = url
r.method = 'DELETE'
r.headers = headers
r.cookiejar = cookies
r.auth = _detect_auth(url, auth)
r.send()
return r.response
return request('DELETE', url, params=params, headers=headers, cookies=cookies, auth=auth)
def add_autoauth(url, authobject):
"""Registers given AuthObject to given URL domain. for auto-activation.
Once a URL is registered with an AuthObject, the configured HTTP
Authentication will be used for all requests with URLS containing the given
URL string.
Example: ::
>>> c_auth = requests.AuthObject('kennethreitz', 'xxxxxxx')
>>> requests.add_autoauth('https://convore.com/api/', c_auth)
>>> r = requests.get('https://convore.com/api/account/verify.json')
# Automatically HTTP Authenticated! Wh00t!
:param url: Base URL for given AuthObject to auto-activate for.
:param authobject: AuthObject to auto-activate.
"""
global AUTOAUTHS
AUTOAUTHS.append((url, authobject))
def _detect_auth(url, auth):
"""Returns registered AuthObject for given url if available, defaulting to
given AuthObject.
"""
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:
return auth
return None
class RequestException(Exception):
"""There was an ambiguous exception that occured while handling your request."""
"""There was an ambiguous exception that occured while handling your
request."""
class AuthenticationError(RequestException):
"""The authentication credentials provided were invalid."""
"""The authentication credentials provided were invalid."""
class URLRequired(RequestException):
"""A valid URL is required to make a request."""
"""A valid URL is required to make a request."""
class InvalidMethod(RequestException):
"""An inappropriate method was attempted."""
"""An inappropriate method was attempted."""

View File

@@ -1 +1,3 @@
from __future__ import absolute_import
from . import poster

View File

@@ -26,6 +26,8 @@ New releases of poster will always have a version number that compares greater
than an older version of poster.
New in version 0.6."""
from __future__ import absolute_import
from . import streaminghttp
from . import encode

View File

@@ -181,16 +181,18 @@ if hasattr(httplib, 'HTTPS'):
return urllib2.HTTPSHandler.do_request_(self, req)
def get_handlers():
handlers = [StreamingHTTPHandler, StreamingHTTPRedirectHandler]
if hasattr(httplib, "HTTPS"):
handlers.append(StreamingHTTPSHandler)
return handlers
def register_openers():
"""Register the streaming http handlers in the global urllib2 default
opener object.
Returns the created OpenerDirector object."""
handlers = [StreamingHTTPHandler, StreamingHTTPRedirectHandler]
if hasattr(httplib, "HTTPS"):
handlers.append(StreamingHTTPSHandler)
opener = urllib2.build_opener(*handlers)
opener = urllib2.build_opener(*get_handlers())
urllib2.install_opener(opener)

View File

@@ -3,49 +3,51 @@
import os
import sys
import requests
from distutils.core import setup
if sys.argv[-1] == "publish":
os.system("python setup.py sdist upload")
sys.exit()
os.system("python setup.py sdist upload")
sys.exit()
if sys.argv[-1] == "test":
os.system("python test_requests.py")
sys.exit()
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',
version='0.2.2',
description='Awesome Python HTTP Library that\'s actually usable.',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='https://github.com/kennethreitz/requests',
packages= [
'requests',
'requests.packages',
'requests.packages.poster'
],
install_requires=required,
license='ISC',
classifiers=(
# 'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: ISC License (ISCL)',
'Programming Language :: Python',
# 'Programming Language :: Python :: 2.5',
name='requests',
version=requests.__version__,
description='Awesome Python HTTP Library that\'s actually usable.',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='https://github.com/kennethreitz/requests',
packages= [
'requests',
'requests.packages',
'requests.packages.poster'
],
install_requires=required,
license='ISC',
classifiers=(
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: ISC License (ISCL)',
'Programming Language :: Python',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
# 'Programming Language :: Python :: 3.0',
# 'Programming Language :: Python :: 3.1',
),
'Programming Language :: Python :: 2.7',
# 'Programming Language :: Python :: 3.0',
# 'Programming Language :: Python :: 3.1',
),
)

156
test_requests.py Normal file → Executable file
View File

@@ -1,61 +1,143 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from cStringIO import StringIO
import cookielib
import requests
class RequestsTestSuite(unittest.TestCase):
"""Requests test cases."""
def setUp(self):
pass
"""Requests test cases."""
def tearDown(self):
"""Teardown."""
pass
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def setUp(self):
pass
def test_HTTP_200_OK_GET(self):
r = requests.get('http://google.com')
self.assertEqual(r.status_code, 200)
def tearDown(self):
"""Teardown."""
pass
def test_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
self.assertEqual(r.status_code, 200)
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET(self):
r = requests.get('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_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTPS_200_OK_GET(self):
auth = requests.AuthObject('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
r = requests.get(url, auth=auth)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
self.assertEqual(r.status_code, 200)
r = requests.get('http://www.google.com/search', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_POSTBIN_GET_POST_FILES(self):
def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
r = requests.get('http://google.com/search?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
def test_user_agent_transfers(self):
"""Issue XX"""
heads = {'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'}
post2 = requests.post(bin.url, files={'some': StringIO('data')})
self.assertEqual(post2.status_code, 201)
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)
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 = ('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 200)
r = requests.get(url)
self.assertEqual(r.status_code, 200)
# reset auto authentication
requests.auth_manager.empty()
def test_POSTBIN_GET_POST_FILES(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')})
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()
unittest.main()

11
tox.ini Normal file
View File

@@ -0,0 +1,11 @@
[tox]
envlist = py25,py26,py27,pypy
[testenv]
commands=py.test --junitxml=junit-{envname}.xml
deps =
pytest
[testenv:pypy]
basepython=/usr/bin/pypy-c