Compare commits
342 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15511584d1 | ||
|
|
80cd1ee663 | ||
|
|
3161783e49 | ||
|
|
e9b4defe8d | ||
|
|
a4b87c6fc1 | ||
|
|
cc350d54b7 | ||
|
|
3afd07451c | ||
|
|
27ee6e0f45 | ||
|
|
849934ccd6 | ||
|
|
597f0ae5fd | ||
|
|
2115046bbc | ||
|
|
565745f7b7 | ||
|
|
76b8afee94 | ||
|
|
892fcd6cc0 | ||
|
|
5537902796 | ||
|
|
9518d053a4 | ||
|
|
93c44157ba | ||
|
|
d47522e6fb | ||
|
|
3e233e4856 | ||
|
|
4a7fdef3ea | ||
|
|
98c719f714 | ||
|
|
dda533cf67 | ||
|
|
aa04cc9b42 | ||
|
|
3b085ed20f | ||
|
|
8b640979ee | ||
|
|
c58ddff3a4 | ||
|
|
53a53668d9 | ||
|
|
e7d8982583 | ||
|
|
abc5e27931 | ||
|
|
c75eaec852 | ||
|
|
d9a349c1a1 | ||
|
|
9d143ed653 | ||
|
|
ce4927e5d3 | ||
|
|
17c551539b | ||
|
|
2f55393593 | ||
|
|
efbbb84315 | ||
|
|
96e7a60b8d | ||
|
|
c22b71fc5b | ||
|
|
007963afde | ||
|
|
0708736c45 | ||
|
|
61d50d531c | ||
|
|
a17110b32a | ||
|
|
571d808d25 | ||
|
|
5e6ecdad9f | - | |
|
|
91e75f7215 | ||
|
|
81d599786a | ||
|
|
9471b0ab88 | ||
|
|
470af42bf2 | ||
|
|
0b34812afc | ||
|
|
47a36de74e | ||
|
|
0c21aa5530 | ||
|
|
fe109b27be | ||
|
|
3f873dc222 | ||
|
|
e0ae2b4155 | ||
|
|
5f603ae787 | ||
|
|
d4502aff87 | ||
|
|
3992ed6ad1 | ||
|
|
b72eb53a73 | ||
|
|
4d0c6cefd0 | ||
|
|
3f8f1dc00c | ||
|
|
008c77c911 | ||
|
|
77b37687b6 | ||
|
|
b7307298d2 | ||
|
|
abcf81e2a8 | ||
|
|
e477fce211 | ||
|
|
467766cfe8 | ||
|
|
66391e5c9d | ||
|
|
f7024a6000 | ||
|
|
ec33ce9d29 | ||
|
|
d0aa0175e6 | ||
|
|
d9e5717377 | ||
|
|
7e255177dc | ||
|
|
b0a0b509da | ||
|
|
51038497f6 | ||
|
|
855c273707 | ||
|
|
281ab5a8f9 | ||
|
|
7836536459 | ||
|
|
2a0eff9c71 | ||
|
|
3e8a14e133 | ||
|
|
8ef0e88220 | ||
|
|
e102c1b4ca | ||
|
|
5ba43d0fe5 | ||
|
|
783e540d6d | ||
|
|
24da41005c | ||
|
|
17d3e1d498 | ||
|
|
8d2ab68a97 | ||
|
|
7322aa5b64 | ||
|
|
6b11cf21f3 | ||
|
|
9322d13f61 | ||
|
|
86a06c5c18 | ||
|
|
d0cb56932c | ||
|
|
fc04d8a368 | ||
|
|
519b9cee96 | ||
|
|
efba606e57 | ||
|
|
5bb18810d4 | ||
|
|
853383a8d3 | ||
|
|
f319006813 | ||
|
|
2fa021582b | ||
|
|
0ed641a26e | ||
|
|
c2fd568695 | ||
|
|
8f72c48548 | ||
|
|
08a821381a | ||
|
|
4625445d2a | ||
|
|
ca428504d4 | ||
|
|
88f79b74ce | ||
|
|
647cdd3847 | ||
|
|
c510d21e4e | ||
|
|
49e5df1ff9 | ||
|
|
2357b13824 | ||
|
|
f137db6803 | ||
|
|
2cf0dad573 | ||
|
|
414b9dde84 | ||
|
|
d50742f059 | ||
|
|
3bc9a181a1 | ||
|
|
975720a30c | ||
|
|
1361506ae6 | ||
|
|
23ff27ea4b | ||
|
|
933cbd735b | ||
|
|
ce1945c780 | ||
|
|
f52a413b6a | ||
|
|
ced96912f3 | ||
|
|
f6e97c322b | ||
|
|
c9d61aefe4 | ||
|
|
85c79658ef | ||
|
|
c4c86fd6e8 | ||
|
|
95dcdd8a05 | ||
|
|
43669183ce | ||
|
|
56e58fdcaf | ||
|
|
47378164ef | ||
|
|
9469b4bb69 | ||
|
|
6cead64345 | ||
|
|
773630b010 | ||
|
|
907e529471 | ||
|
|
2d70647f72 | ||
|
|
10cc4673f9 | ||
|
|
629d730da6 | ||
|
|
d4eabaaacc | ||
|
|
5693314a37 | ||
|
|
2e4724ccea | ||
|
|
e7084940e1 | ||
|
|
b141da36a4 | ||
|
|
bb16fe8115 | ||
|
|
a987729712 | ||
|
|
f7cc7b1778 | ||
|
|
e1909375f5 | ||
|
|
cf94c96a68 | ||
|
|
e7e395549f | ||
|
|
e776242de1 | ||
|
|
7b79cea738 | ||
|
|
32a861b2ed | ||
|
|
2ce26d4a1c | ||
|
|
aa0f78b740 | ||
|
|
e4adef5226 | ||
|
|
7dc581fad9 | ||
|
|
0b6dc091f5 | ||
|
|
e1714e678e | ||
|
|
d10b08ff88 | ||
|
|
18c42e88f5 | ||
|
|
f238dffc18 | ||
|
|
5a7ec361f4 | ||
|
|
291d4742d6 | ||
|
|
95ba6fcab2 | ||
|
|
1cdd1d04ce | ||
|
|
3293c0e8f4 | ||
|
|
074150ff2c | ||
|
|
c3c90bb09c | ||
|
|
87560d65ca | ||
|
|
7e4f7fa600 | ||
|
|
7f9c6233bb | ||
|
|
9b6a190652 | ||
|
|
1e1fb1e7a8 | ||
|
|
080db5ee5d | ||
|
|
2edda29448 | ||
|
|
6ba3523a26 | ||
|
|
87e7a04209 | ||
|
|
260e32a7c3 | ||
|
|
5fff2c757b | ||
|
|
30d699b64f | ||
|
|
7d91d3fb5c | ||
|
|
747f882b0e | ||
|
|
369e7cb38f | ||
|
|
04c456b877 | ||
|
|
f408829d51 | ||
|
|
73b414ab45 | ||
|
|
f18c40fd36 | ||
|
|
891e52d723 | ||
|
|
d2ff0f40eb | ||
|
|
787110c853 | ||
|
|
20319044a3 | ||
|
|
6f08c81698 | ||
|
|
868cd3d1cb | ||
|
|
39edf7a7fa | ||
|
|
4cf4362610 | ||
|
|
0b719f037c | ||
|
|
9603589b06 | ||
|
|
5f38c1209e | ||
|
|
749edb3554 | ||
|
|
882b465968 | ||
|
|
784fe8bbdb | ||
|
|
6253cf4c18 | ||
|
|
8521075868 | ||
|
|
2d98ca7477 | ||
|
|
e2d6a92150 | ||
|
|
162b751f6d | ||
|
|
20aed5488b | ||
|
|
2374d0fd7a | ||
|
|
483d00736c | ||
|
|
15de7d9dba | ||
|
|
34a583b821 | ||
|
|
2da86daa3a | ||
|
|
b4eac4c993 | ||
|
|
b4a4f38cce | ||
|
|
8c81cf64de | ||
|
|
00f066a467 | ||
|
|
bdf9f90c8a | ||
|
|
428678b2e4 | ||
|
|
bcd30f05ab | ||
|
|
bd49da067f | ||
|
|
3e7c682e66 | ||
|
|
bbd1e40bd5 | ||
|
|
a25f5ff1dd | ||
|
|
80d860d2d4 | ||
|
|
f31ade335d | ||
|
|
023ab755e5 | ||
|
|
4320f81bf4 | ||
|
|
47f1f28385 | ||
|
|
3d1444b829 | ||
|
|
4d3a9defd2 | ||
|
|
1f1211747b | ||
|
|
45e30048f0 | ||
|
|
245c6edec3 | ||
|
|
cb0a96fbf3 | ||
|
|
fade9e36b6 | ||
|
|
24e2a05605 | ||
|
|
1749260c97 | ||
|
|
3b86c3c0e3 | ||
|
|
8f3da7b72c | ||
|
|
d6cde6a5df | ||
|
|
54fd24b757 | ||
|
|
2153b21152 | ||
|
|
74013d751c | ||
|
|
1fa1cdd2a3 | ||
|
|
cd0d0a2b17 | ||
|
|
e63569041a | ||
|
|
54449483b6 | ||
|
|
52bedf4c0b | ||
|
|
5962c0ea86 | ||
|
|
9b2d7382bf | ||
|
|
d91af4cadf | ||
|
|
7e3d265b0f | ||
|
|
f990a7b453 | ||
|
|
ef02d02b1f | ||
|
|
04c169ecfe | ||
|
|
5593f12347 | ||
|
|
3189d8690e | ||
|
|
b51e042e73 | ||
|
|
82f1320ed5 | ||
|
|
add6feab02 | ||
|
|
670c4aecdd | ||
|
|
215d3984cc | ||
|
|
0b36d925f6 | ||
|
|
bc290f62d7 | ||
|
|
c0faa9e79c | ||
|
|
f887906ec3 | ||
|
|
982fd9d803 | ||
|
|
d64bf501d0 | ||
|
|
49f1ec1efb | ||
|
|
0741448798 | ||
|
|
8a4d5c4683 | ||
|
|
3cdb777c23 | ||
|
|
ddc5452978 | ||
|
|
8aa6fabd4d | ||
|
|
398f5b501f | ||
|
|
8d9bcefa9d | ||
|
|
0b29964bf8 | ||
|
|
06e662be86 | ||
|
|
e153b94190 | ||
|
|
7d0374df2f | ||
|
|
2f60748c73 | ||
|
|
bbd58b922b | ||
|
|
21e51b504e | ||
|
|
ba79a2eb97 | ||
|
|
ef81c16611 | ||
|
|
da26f1f377 | ||
|
|
c8cc2d0421 | ||
|
|
a00dc1abf3 | ||
|
|
59c747b793 | ||
|
|
baddc5b6ad | ||
|
|
637d0330c2 | ||
|
|
21e2d0c34a | ||
|
|
ec5f227381 | ||
|
|
8ffb2462f9 | ||
|
|
12187155b4 | ||
|
|
d7f49f3aa5 | ||
|
|
461497f5be | ||
|
|
7929bb91be | ||
|
|
6e13e7343d | ||
|
|
f9f3b46ef6 | ||
|
|
cc851818c2 | ||
|
|
ba45b9d890 | ||
|
|
9fd70c209b | ||
|
|
b7970c2a46 | ||
|
|
a7d072a7c0 | ||
|
|
054891ab5e | ||
|
|
d18ea48b95 | ||
|
|
d39cde206b | ||
|
|
58d5e0318e | ||
|
|
3e23e48239 | ||
|
|
f1f5788066 | ||
|
|
928facfcd9 | ||
|
|
70857f3097 | ||
|
|
da726fc030 | ||
|
|
874a874ea6 | ||
|
|
0f76e17258 | ||
|
|
6b36911260 | ||
|
|
1ebf3afb24 | ||
|
|
84544b1d01 | ||
|
|
319b570f82 | ||
|
|
f7481adf45 | ||
|
|
9b165629f9 | ||
|
|
79b43b2b6e | ||
|
|
80b71a03ce | ||
|
|
2546bafdde | ||
|
|
d27e4bff1e | ||
|
|
27dd96cf3d | ||
|
|
3e45e4ba51 | ||
|
|
0ee249247a | ||
|
|
0f520a8c69 | ||
|
|
aa33194606 | ||
|
|
51360e1ede | ||
|
|
af0febe8ad | ||
|
|
155863bf71 | ||
|
|
3cec61481c | ||
|
|
5708fe9565 | ||
|
|
c40deefb8a | ||
|
|
cd8ce63a3b | ||
|
|
35beda21b8 | ||
|
|
85b8b2bd44 | ||
|
|
29aa7736ca | ||
|
|
e8ff405fce | ||
|
|
5172d24e66 | ||
|
|
dfe7d877eb |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,3 +4,6 @@ coverage.xml
|
||||
nosetests.xml
|
||||
pylint.txt
|
||||
*.pyc
|
||||
docs/_build
|
||||
toy.py
|
||||
.gitignore
|
||||
|
||||
23
AUTHORS
23
AUTHORS
@@ -18,4 +18,25 @@ Patches and Suggestions
|
||||
- Rob Madole
|
||||
- Aram Dulyan
|
||||
- Johannes Gorset
|
||||
- 村山めがね (Megan Emurayama)
|
||||
- 村山めがね (Megane Murayama)
|
||||
- James Rowe
|
||||
- Daniel Schauenberg
|
||||
- Zbigniew Siciarz
|
||||
- Daniele Tricoli 'Eriol'
|
||||
- Richard Boulton
|
||||
- Miguel Olivares <miguel@moliware.com>
|
||||
- Alberto Paro
|
||||
- Jérémy Bethmont
|
||||
- 潘旭 (Xu Pan)
|
||||
- Tamás Gulácsi
|
||||
- Rubén Abad
|
||||
- Peter Manser
|
||||
- Jeremy Selier
|
||||
- Jens Diemer
|
||||
- Alex <@alopatin>
|
||||
- Tom Hogans <tomhsx@gmail.com>
|
||||
- Armin Ronacher
|
||||
- Shrikant Sharat Kandula
|
||||
- Mikko Ohtamaa
|
||||
- Den Shabalin
|
||||
- Daniel Miller <danielm@vs-networks.com>
|
||||
|
||||
40
AUTHORS.orig
Normal file
40
AUTHORS.orig
Normal file
@@ -0,0 +1,40 @@
|
||||
Requests is written and maintained by Kenneth Reitz and
|
||||
various contributors:
|
||||
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
- Kenneth Reitz <me@kennethreitz.com>
|
||||
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
|
||||
- Various Pocoo Members
|
||||
- Chris Adams
|
||||
- Flavio Percoco Premoli
|
||||
- Dj Gilcrease
|
||||
- Justin Murphy
|
||||
- Rob Madole
|
||||
- Aram Dulyan
|
||||
- Johannes Gorset
|
||||
- 村山めがね (Megane Murayama)
|
||||
- James Rowe
|
||||
- Daniel Schauenberg
|
||||
- Zbigniew Siciarz
|
||||
- Daniele Tricoli 'Eriol'
|
||||
- Richard Boulton
|
||||
- Miguel Olivares <miguel@moliware.com>
|
||||
- Alberto Paro
|
||||
- Jérémy Bethmont
|
||||
- 潘旭 (Xu Pan)
|
||||
- Tamás Gulácsi
|
||||
- Rubén Abad
|
||||
- Peter Manser
|
||||
- Jeremy Selie
|
||||
<<<<<<< HEAD
|
||||
- Jens Diemer
|
||||
- Alex <@alopatin>
|
||||
=======
|
||||
- Tom Hogans <tomhsx@gmail.com>
|
||||
>>>>>>> 0ed641a26ec2200de00e4bbf3d170c767375351e
|
||||
85
HISTORY.rst
85
HISTORY.rst
@@ -1,8 +1,89 @@
|
||||
History
|
||||
-------
|
||||
|
||||
0.3.4
|
||||
+++++
|
||||
0.6.3 (2011-10-13)
|
||||
++++++++++++++++++
|
||||
|
||||
* Beautiful ``requests.async`` module, for making async requests w/ gevent.
|
||||
|
||||
|
||||
0.6.2 (2011-10-09)
|
||||
++++++++++++++++++
|
||||
|
||||
* GET/HEAD obeys allow_redirects=False
|
||||
|
||||
|
||||
0.6.1 (2011-08-20)
|
||||
++++++++++++++++++
|
||||
|
||||
* Enhanced status codes experience ``\o/``
|
||||
* Set a maximum number of redirects (``settings.max_redirects``)
|
||||
* Full Unicode URL support
|
||||
* Support for protocol-less redirects.
|
||||
* Allow for arbitrary request types.
|
||||
* Bugfixes
|
||||
|
||||
|
||||
0.6.0 (2011-08-17)
|
||||
++++++++++++++++++
|
||||
|
||||
* New callback hook system
|
||||
* New persistient sessions object and context manager
|
||||
* Transparent Dict-cookie handling
|
||||
* Status code reference object
|
||||
* Removed Response.cached
|
||||
* Added Response.request
|
||||
* All args are kwargs
|
||||
* Relative redirect support
|
||||
* HTTPError handling improvements
|
||||
* Improved https testing
|
||||
* Bugfixes
|
||||
|
||||
|
||||
0.5.1 (2011-07-23)
|
||||
++++++++++++++++++
|
||||
|
||||
* International Domain Name Support!
|
||||
* Access headers without fetching entire body (``read()``)
|
||||
* Use lists as dicts for parameters
|
||||
* Add Forced Basic Authentication
|
||||
* Forced Basic is default authentication type
|
||||
* ``python-requests.org`` default User-Agent header
|
||||
* CaseInsensitiveDict lower-case caching
|
||||
* Response.history bugfix
|
||||
|
||||
|
||||
0.5.0 (2011-06-21)
|
||||
++++++++++++++++++
|
||||
|
||||
* PATCH Support
|
||||
* Support for Proxies
|
||||
* HTTPBin Test Suite
|
||||
* Redirect Fixes
|
||||
* settings.verbose stream writing
|
||||
* Querystrings for all methods
|
||||
* URLErrors (Connection Refused, Timeout, Invalid URLs) are treated as explicity raised
|
||||
``r.requests.get('hwe://blah'); r.raise_for_status()``
|
||||
|
||||
|
||||
0.4.1 (2011-05-22)
|
||||
++++++++++++++++++
|
||||
|
||||
* Improved Redirection Handling
|
||||
* New 'allow_redirects' param for following non-GET/HEAD Redirects
|
||||
* Settings module refactoring
|
||||
|
||||
|
||||
0.4.0 (2011-05-15)
|
||||
++++++++++++++++++
|
||||
|
||||
* Response.history: list of redirected responses
|
||||
* Case-Insensitive Header Dictionaries!
|
||||
* Unicode URLs
|
||||
|
||||
|
||||
0.3.4 (2011-05-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Urllib2 HTTPAuthentication Recursion fix (Basic/Digest)
|
||||
* Internal Refactor
|
||||
|
||||
14
Makefile
Normal file
14
Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
init:
|
||||
pip install -r reqs.txt
|
||||
|
||||
test:
|
||||
nosetests test_requests.py --processes=30
|
||||
|
||||
ci: init
|
||||
nosetests --search-test --processes=30 --with-nosexunit test_requests.py
|
||||
pyflakes requests | awk -F\: '{printf "%s:%s: [E]%s\n", $1, $2, $3}' > violations.pyflakes.txt
|
||||
|
||||
site:
|
||||
cd docs; make dirhtml
|
||||
|
||||
docs: site
|
||||
124
README.rst
124
README.rst
@@ -1,20 +1,49 @@
|
||||
Requests: The Simple (e.g. usable) HTTP Module
|
||||
==============================================
|
||||
Requests: HTTP for Humans
|
||||
=========================
|
||||
|
||||
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).
|
||||
Requests is an ISC Licensed HTTP library, written in Python, for human
|
||||
beings.
|
||||
|
||||
But this one's different. This one's going to be awesome. And simple.
|
||||
Most existing Python modules for sending HTTP requests are extremely
|
||||
verbose and cumbersome. Python's builtin urllib2 module provides most of
|
||||
the HTTP capabilities you should need, but the api is thoroughly broken.
|
||||
It requires an enormous amount of work (even method overrides) to
|
||||
perform the simplest of tasks.
|
||||
|
||||
Things shouldn't be this way. Not in Python.
|
||||
|
||||
::
|
||||
|
||||
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
204
|
||||
>>> r.headers['content-type']
|
||||
'application/json'
|
||||
>>> r.content
|
||||
...
|
||||
|
||||
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 urllib2, but it does
|
||||
all the hard work and crazy hacks for you.
|
||||
|
||||
Really simple.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Extremely simple GET, HEAD, POST, PUT, DELETE Requests
|
||||
- Extremely simple HEAD, GET, POST, PUT, PATCH, DELETE Requests
|
||||
+ Simple HTTP Header Request Attachment
|
||||
+ Simple Data/Params Request Attachment
|
||||
+ Simple Multipart File Uploads
|
||||
+ CookieJar Support
|
||||
+ Redirection History
|
||||
+ Proxy Support
|
||||
+ Redirection Recursion Urllib Fix
|
||||
+ Auto Decompression of GZipped Content
|
||||
+ Unicode URL Support
|
||||
|
||||
- Simple Authentication
|
||||
+ Simple URL + HTTP Auth Registry
|
||||
@@ -31,15 +60,14 @@ It couldn't be simpler. ::
|
||||
|
||||
HTTPS? Basic Authentication? ::
|
||||
|
||||
>>> r = requests.get('https://convore.com/api/account/verify.json')
|
||||
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass')
|
||||
>>> r.status_code
|
||||
401
|
||||
|
||||
|
||||
Uh oh, we're not authorized! Let's add authentication. ::
|
||||
|
||||
>>> conv_auth = ('requeststest', 'requeststest')
|
||||
>>> r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
|
||||
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass', auth=('user', 'pass'))
|
||||
|
||||
>>> r.status_code
|
||||
200
|
||||
@@ -48,74 +76,7 @@ Uh oh, we're not authorized! Let's add authentication. ::
|
||||
'application/json'
|
||||
|
||||
>>> r.content
|
||||
'{"username": "requeststest", "url": "/users/requeststest/", "id": "9408", "img": "censored-long-url"}'
|
||||
|
||||
|
||||
|
||||
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
|
||||
>>> requests.get(url, params={}, headers={}, cookies=None, auth=None)
|
||||
<Response [200]>
|
||||
|
||||
HEAD Requests
|
||||
>>> requests.head(url, params={}, headers={}, cookies=None, auth=None)
|
||||
<Response [200]>
|
||||
|
||||
PUT Requests
|
||||
>>> requests.put(url, data='', headers={}, files={}, cookies=None, auth=None)
|
||||
<Response [200]>
|
||||
|
||||
POST Requests
|
||||
>>> requests.post(url, data={}, headers={}, files={}, cookies=None, auth=None)
|
||||
<Response [200]>
|
||||
|
||||
DELETE Requests
|
||||
>>> requests.delete(url, params={}, headers={}, cookies=None, auth=None)
|
||||
<Response [200]>
|
||||
|
||||
|
||||
**Responses:**
|
||||
|
||||
Response.status_code
|
||||
(Integer) Received HTTP Status Code Response
|
||||
|
||||
Response.headers
|
||||
(Dictionary) Received HTTP Response Headers
|
||||
|
||||
Response.content
|
||||
(Bytes) Received Content
|
||||
|
||||
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.auth_manager.add_auth(url, authobject)
|
||||
|
||||
'{"authenticated": true, "user": "user"}'
|
||||
|
||||
|
||||
Installation
|
||||
@@ -139,14 +100,5 @@ Contribute
|
||||
If you'd like to contribute, simply fork `the repository`_, commit your changes to the **develop** branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS_.
|
||||
|
||||
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
- 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
|
||||
|
||||
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
python-requests (0.4.1-0) testing; urgency=low
|
||||
|
||||
* Initial Debian package
|
||||
|
||||
-- Bruno Clermont <bruno.clermont@gmail.com> Thu, 26 May 2011 16:25:00 -0500
|
||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
5
|
||||
13
debian/control
vendored
Normal file
13
debian/control
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Source: python-requests
|
||||
Section: python
|
||||
Priority: optional
|
||||
Maintainer: Bruno Clermont <bruno.clermont@gmail.com>
|
||||
Homepage: https://github.com/bclermont/requests
|
||||
Bugs: https://github.com/bclermont/requests/issues
|
||||
Build-Depends: debhelper, python-support
|
||||
|
||||
Package: python-requests
|
||||
Architecture: all
|
||||
Depends: ${python:Depends}, python-support
|
||||
Provides: ${python:Provides}
|
||||
Description: Python HTTP Requests for Humans.
|
||||
1
debian/docs
vendored
Normal file
1
debian/docs
vendored
Normal file
@@ -0,0 +1 @@
|
||||
docs/user/*.rst
|
||||
1
debian/pyversions
vendored
Normal file
1
debian/pyversions
vendored
Normal file
@@ -0,0 +1 @@
|
||||
2.4-
|
||||
42
debian/rules
vendored
Executable file
42
debian/rules
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Verbose mode
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
|
||||
rm -rf build requests.egg-info
|
||||
# find django-sentry/ -name *.pyc | xargs rm -f
|
||||
|
||||
dh_clean
|
||||
|
||||
build:
|
||||
dh_testdir
|
||||
|
||||
python setup.py build
|
||||
|
||||
install:
|
||||
dh_testdir
|
||||
dh_installdirs
|
||||
|
||||
python setup.py install --root $(CURDIR)/debian/python-requests
|
||||
|
||||
binary-indep: install
|
||||
|
||||
binary-arch: install
|
||||
dh_install
|
||||
dh_installdocs
|
||||
# dh_installchangelogs
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
dh_pysupport
|
||||
dh_gencontrol
|
||||
dh_installdeb
|
||||
dh_md5sums
|
||||
dh_builddeb -- -Z lzma -z9
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary
|
||||
|
||||
BIN
docs/_static/requests-sidebar.png
vendored
Normal file
BIN
docs/_static/requests-sidebar.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
27
docs/_templates/sidebarintro.html
vendored
Normal file
27
docs/_templates/sidebarintro.html
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Requests is an elegant and simple HTTP library for Python, built for
|
||||
human beings. You are currently looking at the documentation of the
|
||||
development release.
|
||||
</p>
|
||||
|
||||
<h3>Feedback</h3>
|
||||
<p>
|
||||
Feedback is greatly appreciated. If you have any questions, comments,
|
||||
random praise, or anonymous threats, <a href="mailto:me@kennethreitz.com">
|
||||
shoot me an email</a>.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://python-requests.org/">The Requests Website</a></li>
|
||||
<li><a href="http://pypi.python.org/pypi/requests">Requests @ PyPI</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/requests">Requests @ GitHub</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/requests/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
11
docs/_templates/sidebarlogo.html
vendored
Normal file
11
docs/_templates/sidebarlogo.html
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Requests is an elegant and simple HTTP library for Python, built for
|
||||
human beings. You are currently looking at the documentation of the
|
||||
development release.
|
||||
</p>
|
||||
3
docs/_themes/.gitignore
vendored
Normal file
3
docs/_themes/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
.DS_Store
|
||||
45
docs/_themes/LICENSE
vendored
Normal file
45
docs/_themes/LICENSE
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
Modifications:
|
||||
|
||||
Copyright (c) 2011 Kenneth Reitz.
|
||||
|
||||
|
||||
Original Project:
|
||||
|
||||
Copyright (c) 2010 by Armin Ronacher.
|
||||
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms of the theme, with or
|
||||
without modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
We kindly ask you to only use these themes in an unmodified manner just
|
||||
for Flask and Flask-related products, not for unrelated projects. If you
|
||||
like the visual style and want to use it for your own projects, please
|
||||
consider making some larger changes to the themes (such as changing
|
||||
font faces, sizes, colors or margins).
|
||||
|
||||
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
25
docs/_themes/README.rst
vendored
Normal file
25
docs/_themes/README.rst
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
krTheme Sphinx Style
|
||||
====================
|
||||
|
||||
This repository contains sphinx styles Kenneth Reitz uses in most of
|
||||
his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related
|
||||
projects. To use this style in your Sphinx documentation, follow
|
||||
this guide:
|
||||
|
||||
1. put this folder as _themes into your docs folder. Alternatively
|
||||
you can also use git submodules to check out the contents there.
|
||||
|
||||
2. add this to your conf.py: ::
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'flask'
|
||||
|
||||
The following themes exist:
|
||||
|
||||
**kr**
|
||||
the standard flask documentation theme for large projects
|
||||
|
||||
**kr_small**
|
||||
small one-page theme. Intended to be used by very small addon libraries.
|
||||
|
||||
86
docs/_themes/flask_theme_support.py
vendored
Normal file
86
docs/_themes/flask_theme_support.py
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
# flasky extensions. flasky pygments style based on tango style
|
||||
from pygments.style import Style
|
||||
from pygments.token import Keyword, Name, Comment, String, Error, \
|
||||
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
|
||||
|
||||
|
||||
class FlaskyStyle(Style):
|
||||
background_color = "#f8f8f8"
|
||||
default_style = ""
|
||||
|
||||
styles = {
|
||||
# No corresponding class for the following:
|
||||
#Text: "", # class: ''
|
||||
Whitespace: "underline #f8f8f8", # class: 'w'
|
||||
Error: "#a40000 border:#ef2929", # class: 'err'
|
||||
Other: "#000000", # class 'x'
|
||||
|
||||
Comment: "italic #8f5902", # class: 'c'
|
||||
Comment.Preproc: "noitalic", # class: 'cp'
|
||||
|
||||
Keyword: "bold #004461", # class: 'k'
|
||||
Keyword.Constant: "bold #004461", # class: 'kc'
|
||||
Keyword.Declaration: "bold #004461", # class: 'kd'
|
||||
Keyword.Namespace: "bold #004461", # class: 'kn'
|
||||
Keyword.Pseudo: "bold #004461", # class: 'kp'
|
||||
Keyword.Reserved: "bold #004461", # class: 'kr'
|
||||
Keyword.Type: "bold #004461", # class: 'kt'
|
||||
|
||||
Operator: "#582800", # class: 'o'
|
||||
Operator.Word: "bold #004461", # class: 'ow' - like keywords
|
||||
|
||||
Punctuation: "bold #000000", # class: 'p'
|
||||
|
||||
# because special names such as Name.Class, Name.Function, etc.
|
||||
# are not recognized as such later in the parsing, we choose them
|
||||
# to look the same as ordinary variables.
|
||||
Name: "#000000", # class: 'n'
|
||||
Name.Attribute: "#c4a000", # class: 'na' - to be revised
|
||||
Name.Builtin: "#004461", # class: 'nb'
|
||||
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
|
||||
Name.Class: "#000000", # class: 'nc' - to be revised
|
||||
Name.Constant: "#000000", # class: 'no' - to be revised
|
||||
Name.Decorator: "#888", # class: 'nd' - to be revised
|
||||
Name.Entity: "#ce5c00", # class: 'ni'
|
||||
Name.Exception: "bold #cc0000", # class: 'ne'
|
||||
Name.Function: "#000000", # class: 'nf'
|
||||
Name.Property: "#000000", # class: 'py'
|
||||
Name.Label: "#f57900", # class: 'nl'
|
||||
Name.Namespace: "#000000", # class: 'nn' - to be revised
|
||||
Name.Other: "#000000", # class: 'nx'
|
||||
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
|
||||
Name.Variable: "#000000", # class: 'nv' - to be revised
|
||||
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
|
||||
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
|
||||
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
|
||||
|
||||
Number: "#990000", # class: 'm'
|
||||
|
||||
Literal: "#000000", # class: 'l'
|
||||
Literal.Date: "#000000", # class: 'ld'
|
||||
|
||||
String: "#4e9a06", # class: 's'
|
||||
String.Backtick: "#4e9a06", # class: 'sb'
|
||||
String.Char: "#4e9a06", # class: 'sc'
|
||||
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
|
||||
String.Double: "#4e9a06", # class: 's2'
|
||||
String.Escape: "#4e9a06", # class: 'se'
|
||||
String.Heredoc: "#4e9a06", # class: 'sh'
|
||||
String.Interpol: "#4e9a06", # class: 'si'
|
||||
String.Other: "#4e9a06", # class: 'sx'
|
||||
String.Regex: "#4e9a06", # class: 'sr'
|
||||
String.Single: "#4e9a06", # class: 's1'
|
||||
String.Symbol: "#4e9a06", # class: 'ss'
|
||||
|
||||
Generic: "#000000", # class: 'g'
|
||||
Generic.Deleted: "#a40000", # class: 'gd'
|
||||
Generic.Emph: "italic #000000", # class: 'ge'
|
||||
Generic.Error: "#ef2929", # class: 'gr'
|
||||
Generic.Heading: "bold #000080", # class: 'gh'
|
||||
Generic.Inserted: "#00A000", # class: 'gi'
|
||||
Generic.Output: "#888", # class: 'go'
|
||||
Generic.Prompt: "#745334", # class: 'gp'
|
||||
Generic.Strong: "bold #000000", # class: 'gs'
|
||||
Generic.Subheading: "bold #800080", # class: 'gu'
|
||||
Generic.Traceback: "bold #a40000", # class: 'gt'
|
||||
}
|
||||
47
docs/_themes/kr/layout.html
vendored
Normal file
47
docs/_themes/kr/layout.html
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{%- extends "basic/layout.html" %}
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
{% if theme_touch_icon %}
|
||||
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
|
||||
{% endif %}
|
||||
<link media="only screen and (max-device-width: 480px)" href="{{
|
||||
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
{%- block relbar2 %}{% endblock %}
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
© Copyright {{ copyright }}.
|
||||
</div>
|
||||
<a href="https://github.com/kennethreitz/requests">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-8742933-11']);
|
||||
_gaq.push(['_setDomainName', 'none']);
|
||||
_gaq.push(['_setAllowLinker', true]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var t = document.createElement('script');
|
||||
t.type = 'text/javascript';
|
||||
t.async = true;
|
||||
t.id = 'gauges-tracker';
|
||||
t.setAttribute('data-site-id',
|
||||
'4ddc27f6613f5d186d000007');
|
||||
t.src = '//secure.gaug.es/track.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(t, s);
|
||||
})();
|
||||
</script>
|
||||
{%- endblock %}
|
||||
19
docs/_themes/kr/relations.html
vendored
Normal file
19
docs/_themes/kr/relations.html
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
|
||||
{%- endfor %}
|
||||
{%- if prev %}
|
||||
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
|
||||
}}">{{ prev.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
|
||||
}}">{{ next.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- for parent in parents %}
|
||||
</ul></li>
|
||||
{%- endfor %}
|
||||
</ul></li>
|
||||
</ul>
|
||||
469
docs/_themes/kr/static/flasky.css_t
vendored
Normal file
469
docs/_themes/kr/static/flasky.css_t
vendored
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
{% set page_width = '940px' %}
|
||||
{% set sidebar_width = '220px' %}
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
|
||||
font-size: 17px;
|
||||
background-color: white;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: {{ page_width }};
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ sidebar_width }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: {{ sidebar_width }};
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 0 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: {{ page_width }};
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0;
|
||||
margin: -10px 0 0 -20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #555;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #ccc;
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #ddd;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition a tt {
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
background: #fdfdfd;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: 0px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 7px 30px;
|
||||
margin: 15px -30px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* scrollbars */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button:start:decrement,
|
||||
::-webkit-scrollbar-button:end:increment {
|
||||
display: block;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button:vertical:increment {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: #eee;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:vertical {
|
||||
height: 50px;
|
||||
background-color: #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:horizontal {
|
||||
width: 50px;
|
||||
background-color: #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
/* misc. */
|
||||
|
||||
.revsys-inline {
|
||||
display: none!important;
|
||||
}
|
||||
70
docs/_themes/kr/static/small_flask.css
vendored
Normal file
70
docs/_themes/kr/static/small_flask.css
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* small_flask.css_t
|
||||
* ~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
margin: 50px -30px -20px -30px;
|
||||
padding: 10px 20px;
|
||||
background: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
div.related ul,
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
7
docs/_themes/kr/theme.conf
vendored
Normal file
7
docs/_themes/kr/theme.conf
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
touch_icon =
|
||||
22
docs/_themes/kr_small/layout.html
vendored
Normal file
22
docs/_themes/kr_small/layout.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "basic/layout.html" %}
|
||||
{% block header %}
|
||||
{{ super() }}
|
||||
{% if pagename == 'index' %}
|
||||
<div class=indexwrapper>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block footer %}
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{# do not display relbars #}
|
||||
{% block relbar1 %}{% endblock %}
|
||||
{% block relbar2 %}
|
||||
{% if theme_github_fork %}
|
||||
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
|
||||
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block sidebar1 %}{% endblock %}
|
||||
{% block sidebar2 %}{% endblock %}
|
||||
287
docs/_themes/kr_small/static/flasky.css_t
vendored
Normal file
287
docs/_themes/kr_small/static/flasky.css_t
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- flasky theme based on nature theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 17px;
|
||||
color: #000;
|
||||
background: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 40px auto 0 auto;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 30px 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
text-align: right;
|
||||
color: #888;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
width: 650px;
|
||||
margin: 0 auto 40px auto;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
line-height: 32px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
padding: 0 0 0 10px;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body {
|
||||
padding-bottom: 40px; /* saved for footer */
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
{% if theme_index_logo %}
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url({{ theme_index_logo }}) no-repeat center center;
|
||||
height: {{ theme_index_logo_height }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: white;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
margin: 15px -30px;
|
||||
padding: 8px;
|
||||
line-height: 1.3em;
|
||||
padding: 7px 30px;
|
||||
background: #eee;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
dl pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
10
docs/_themes/kr_small/theme.conf
vendored
Normal file
10
docs/_themes/kr_small/theme.conf
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
nosidebar = true
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
index_logo = ''
|
||||
index_logo_height = 120px
|
||||
github_fork = ''
|
||||
93
docs/api.rst
Normal file
93
docs/api.rst
Normal file
@@ -0,0 +1,93 @@
|
||||
.. _api:
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. module:: requests
|
||||
|
||||
This part of the documentation covers all the interfaces of Requests. For
|
||||
parts where Requests depends on external libraries, we document the most
|
||||
important right here and provide links to the canonical documentation.
|
||||
|
||||
|
||||
Main Interface
|
||||
--------------
|
||||
|
||||
All of Request's functionality can be accessed by these 7 methods.
|
||||
They all return an instance of the :class:`Response <Response>` object.
|
||||
|
||||
.. autofunction:: request
|
||||
.. autofunction:: head
|
||||
.. autofunction:: get
|
||||
.. autofunction:: post
|
||||
.. autofunction:: put
|
||||
.. autofunction:: patch
|
||||
.. autofunction:: delete
|
||||
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
.. autoclass:: Response
|
||||
:inherited-members:
|
||||
|
||||
|
||||
|
||||
Utilities
|
||||
---------
|
||||
|
||||
These functions are used internally, but may be useful outside of
|
||||
Requests.
|
||||
|
||||
.. module:: requests.utils
|
||||
|
||||
Cookies
|
||||
~~~~~~~
|
||||
|
||||
.. autofunction:: dict_from_cookiejar
|
||||
.. autofunction:: cookiejar_from_dict
|
||||
.. autofunction:: add_dict_to_cookiejar
|
||||
|
||||
Curl
|
||||
~~~~
|
||||
.. autofunction:: curl_from_request
|
||||
|
||||
Encodings
|
||||
~~~~~~~~~
|
||||
|
||||
.. autofunction:: get_encodings_from_content
|
||||
.. autofunction:: get_encoding_from_headers
|
||||
.. autofunction:: get_unicode_from_response
|
||||
.. autofunction:: decode_gzip
|
||||
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
These items are an internal component to Requests, and should never be
|
||||
seen by the end user (developer). This part of the API documentation
|
||||
exists for those who are extending the functionality of Requests.
|
||||
|
||||
Exceptions
|
||||
~~~~~~~~~~
|
||||
|
||||
.. module:: requests
|
||||
|
||||
.. autoexception:: HTTPError
|
||||
|
||||
.. autoexception:: RequestException
|
||||
|
||||
.. autoexception:: AuthenticationError
|
||||
.. autoexception:: URLRequired
|
||||
.. autoexception:: InvalidMethod
|
||||
.. autoexception:: TooManyRedirects
|
||||
|
||||
|
||||
|
||||
Classes
|
||||
~~~~~~~
|
||||
|
||||
.. autoclass:: requests.Request
|
||||
:inherited-members:
|
||||
|
||||
|
||||
83
docs/community/faq.rst
Normal file
83
docs/community/faq.rst
Normal file
@@ -0,0 +1,83 @@
|
||||
.. _faq:
|
||||
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
This part of the documentation answers common questions about Requests.
|
||||
|
||||
Encoded Data?
|
||||
-------------
|
||||
|
||||
Requests automatically decompresses gzip-encoded responses, and does
|
||||
its best to decodes response content to unicode when possible.
|
||||
|
||||
You can get direct access to the raw response (and even the socket),
|
||||
if needed as well.
|
||||
|
||||
|
||||
Custom User-Agents?
|
||||
-------------------
|
||||
|
||||
Requests allows you to easily override User-Agent strings, along with
|
||||
any other HTTP Header.
|
||||
|
||||
|
||||
Why not Httplib2?
|
||||
-----------------
|
||||
|
||||
Chris Adams gave an excellent summary on
|
||||
`Hacker News <http://news.ycombinator.com/item?id=2884406>`_:
|
||||
|
||||
httplib2 is part of why you should use requests: it's far more respectable
|
||||
as a client but not as well documented and it still takes way too much code
|
||||
for basic operations. I appreciate what httplib2 is trying to do, that
|
||||
there's a ton of hard low-level annoyances in building a modern HTTP
|
||||
client, but really, just use requests instead. Kenneth Reitz is very
|
||||
motivated and he gets the degree to which simple things should be simple
|
||||
whereas httplib2 feels more like an academic exercise than something
|
||||
people should use to build production systems[1].
|
||||
|
||||
Disclosure: I'm listed in the requests AUTHORS file but can claim credit
|
||||
for, oh, about 0.0001% of the awesomeness.
|
||||
|
||||
1. http://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
|
||||
an annoying bug which affect many people, there was a fix available for
|
||||
months, which worked great when I applied it in a fork and pounded a couple
|
||||
TB of data through it, but it took over a year to make it into trunk and
|
||||
even longer to make it onto PyPI where any other project which required "
|
||||
httplib2" would get the working version.
|
||||
|
||||
|
||||
Python 3 Support?
|
||||
-----------------
|
||||
|
||||
It's on the way. Here's a list of Python platforms that are officially
|
||||
supported:
|
||||
|
||||
* cPython 2.5
|
||||
* cPython 2.5.5
|
||||
* cPython 2.5.6
|
||||
* cPython 2.6
|
||||
* cPython 2.6.6
|
||||
* cPython 2.6.7
|
||||
* cPython 2.7
|
||||
* cPython 2.7.1
|
||||
* cPython 2.7.2
|
||||
* PyPy-c 1.4
|
||||
* PyPy-c 1.5
|
||||
|
||||
|
||||
Support for Python 3.x is coming *very* soon.
|
||||
|
||||
|
||||
Keep-alive Support?
|
||||
-------------------
|
||||
|
||||
It's on the way.
|
||||
|
||||
|
||||
Proxy Support?
|
||||
--------------
|
||||
|
||||
You bet!
|
||||
|
||||
37
docs/community/support.rst
Normal file
37
docs/community/support.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
.. _support:
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
If you have a questions or issues about Requests, there are serveral options:
|
||||
|
||||
Send a Tweet
|
||||
------------
|
||||
|
||||
If your question is less than 140 characters, feel free to send a tweet to
|
||||
`@kennethreitz <http://twitter.com/kennethreitz>`_.
|
||||
|
||||
|
||||
File an Issue
|
||||
-------------
|
||||
|
||||
If you notice some unexpected behavior in Requests, or want to see support
|
||||
for a new feature,
|
||||
`file an issue on GitHub <https://github.com/kennethreitz/requests/issues>`_.
|
||||
|
||||
|
||||
E-mail
|
||||
------
|
||||
|
||||
I'm more than happy to answer any personal or in-depth questions about
|
||||
Requests. Feel free to email
|
||||
`requests@kennethreitz.com <mailto:requests@kennethreitz.com>`_.
|
||||
|
||||
|
||||
IRC
|
||||
---
|
||||
|
||||
The official Freenode channel for Requests is
|
||||
`#python-requests <irc://irc.freenode.net/python-requests>`_
|
||||
|
||||
I'm also available as **kennethreitz** on Freenode.
|
||||
30
docs/community/updates.rst
Normal file
30
docs/community/updates.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
.. _updates:
|
||||
|
||||
Updates
|
||||
=======
|
||||
|
||||
If you'd like to stay up to date on the community and development of Requests,
|
||||
there are serveral options:
|
||||
|
||||
GitHub
|
||||
------
|
||||
|
||||
The best way to track the development of Requests is through
|
||||
`the GitHub repo <https://github.com/kennethreitz/requests>`_.
|
||||
|
||||
Twitter
|
||||
-------
|
||||
|
||||
I often tweet about new features and releases of Requests.
|
||||
|
||||
Follow `@kennethreitz <https://twitter.com/kennethreitz>`_ for updates.
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
There's a low-volume mailing list for Requests. To subscribe to the
|
||||
mailing list, send an email to
|
||||
`requests@librelist.org <mailto:requests@librelist.org>`_.
|
||||
|
||||
|
||||
26
docs/conf.py
26
docs/conf.py
@@ -16,7 +16,9 @@ import sys, os
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
import requests
|
||||
from requests import __version__
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
@@ -41,14 +43,14 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Requests'
|
||||
copyright = u'2011, Kenneth Reitz'
|
||||
copyright = u'2011. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.3.3'
|
||||
version = __version__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
@@ -81,7 +83,7 @@ exclude_patterns = ['_build']
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
@@ -112,6 +114,7 @@ html_theme = 'default'
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
@@ -131,7 +134,12 @@ html_static_path = ['_static']
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
@@ -147,10 +155,10 @@ html_static_path = ['_static']
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
html_show_sourcelink = False
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
html_show_sphinx = False
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
@@ -230,3 +238,7 @@ texinfo_documents = [
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
texinfo_appendices = []
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'kr'
|
||||
5
docs/dev/authors.rst
Normal file
5
docs/dev/authors.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
Authors
|
||||
=======
|
||||
|
||||
|
||||
.. include:: ../../AUTHORS
|
||||
0
docs/dev/internals.rst
Normal file
0
docs/dev/internals.rst
Normal file
15
docs/dev/todo.rst
Normal file
15
docs/dev/todo.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
How to Help
|
||||
===========
|
||||
|
||||
Requests is under active development, and contributions are more than welcome!
|
||||
|
||||
|
||||
What Needs to be Done
|
||||
---------------------
|
||||
|
||||
- OAuth Support (`#65 <https://github.com/kennethreitz/requests/issues/65>`_)
|
||||
- HTTP Cert Checking (`#30 <https://github.com/kennethreitz/requests/issues/30>`_)
|
||||
- Removal of Poster (`#68 <https://github.com/kennethreitz/requests/issues/68>`_)
|
||||
- Python 3.x Support (`#70 <https://github.com/kennethreitz/requests/issues/70>`_)
|
||||
- Kerberos Support (`#47 <https://github.com/kennethreitz/requests/issues/47>`_)
|
||||
- More Flexible Context Manager (`#69 <https://github.com/kennethreitz/requests/issues/69>`_)
|
||||
112
docs/index.rst
112
docs/index.rst
@@ -3,18 +3,114 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Requests's documentation!
|
||||
====================================
|
||||
Requests: HTTP for Humans
|
||||
=========================
|
||||
|
||||
Contents:
|
||||
Release v\ |version|. (:ref:`Installation <install>`)
|
||||
|
||||
Requests is an :ref:`ISC Licensed <isc>` HTTP library, written in Python, for human beings.
|
||||
|
||||
Most existing Python modules for sending HTTP requests are extremely verbose
|
||||
and cumbersome. Python's builtin **urllib2** module provides most of
|
||||
the HTTP capabilities you should need, but the api is thoroughly **broken**.
|
||||
It requires an *enormous* amount of work (even method overrides) to perform
|
||||
the simplest of tasks.
|
||||
|
||||
Things shouldn’t be this way. Not in Python.
|
||||
|
||||
::
|
||||
|
||||
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
204
|
||||
>>> r.headers['content-type']
|
||||
'application/json'
|
||||
>>> r.content
|
||||
...
|
||||
|
||||
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 :py:class:`urllib2`, but it does
|
||||
all the hard work and crazy hacks for you.
|
||||
|
||||
Testimonials
|
||||
------------
|
||||
|
||||
`Twitter, Inc <http://twitter.com>`_,
|
||||
a U.S. Federal Institution,
|
||||
`Readability <http://readability.com>`_, and
|
||||
`Work for Pie <http://workforpie.com>`_
|
||||
use Requests internally.
|
||||
|
||||
**Armin Ronacher**
|
||||
Requests is the perfect example how beautiful an API can be with the
|
||||
right level of abstraction.
|
||||
|
||||
**Daniel Greenfeld**
|
||||
Nuked a 1200 LOC spaghetti code library with 10 lines of code thanks to
|
||||
@kennethreitz's request library. Today has been AWESOME.
|
||||
|
||||
**Kenny Meyers**
|
||||
Python HTTP: When in doubt, or when not in doubt, use Requests. Beautiful,
|
||||
simple, Pythonic.
|
||||
|
||||
**Rich Leland**
|
||||
Requests is awesome. That is all.
|
||||
|
||||
|
||||
User Guide
|
||||
----------
|
||||
|
||||
This part of the documentation, which is mostly prose, begins with some
|
||||
background information about Requests, then focuses on step-by-step
|
||||
instructions for getting the most out of Requests.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
user/intro
|
||||
user/install
|
||||
user/quickstart
|
||||
user/advanced
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
Community Guide
|
||||
-----------------
|
||||
|
||||
This part of the documentation, which is mostly prose, details the
|
||||
Requests ecosystem and community.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
community/faq
|
||||
community/support
|
||||
community/updates
|
||||
|
||||
API Documentation
|
||||
-----------------
|
||||
|
||||
If you are looking for information on a specific function, class or method,
|
||||
this part of the documentation is for you.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api
|
||||
|
||||
|
||||
Developer Guide
|
||||
---------------
|
||||
|
||||
If you want to contribute to the project, this part of the documentation is for
|
||||
you.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
dev/internals
|
||||
dev/todo
|
||||
dev/authors
|
||||
|
||||
171
docs/user/advanced.rst
Normal file
171
docs/user/advanced.rst
Normal file
@@ -0,0 +1,171 @@
|
||||
.. _advanced:
|
||||
|
||||
Advanced Usage
|
||||
==============
|
||||
|
||||
This document covers some of Requests more advanced features.
|
||||
|
||||
|
||||
Session Objects
|
||||
---------------
|
||||
|
||||
The Session object allows you to persist certain parameters across
|
||||
requests. It also establishes a CookieJar and passes it along
|
||||
to any requests made from the Session instance.
|
||||
|
||||
A session object has all the methods of the main Requests API.
|
||||
|
||||
Let's persist some cookies across requests::
|
||||
|
||||
with requests.session() as s:
|
||||
|
||||
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
|
||||
r = s.get("http://httpbin.org/cookies")
|
||||
|
||||
print r.content
|
||||
# '{"cookies": {"sessioncookie": "123456789"}}'
|
||||
|
||||
|
||||
Sessions can also be used to provide default data to the request methods::
|
||||
|
||||
headers = {'x-test': 'true'}
|
||||
auth = ('user', 'pass')
|
||||
|
||||
with requests.session(auth=auth, headers=headers) as c:
|
||||
|
||||
# both 'x-test' and 'x-test2' are sent
|
||||
c.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
|
||||
|
||||
|
||||
.. admonition:: Global Settings
|
||||
|
||||
Certain parameters are best set at the ``request.config`` level
|
||||
(e.g.. a global proxy, user agent header).
|
||||
|
||||
|
||||
Asynchronous Requests
|
||||
----------------------
|
||||
|
||||
Requests has first-class support for non-blocking i/o requests, powered
|
||||
by gevent. This allows you to send a bunch of HTTP requests at the same
|
||||
|
||||
First, let's import the async module. Heads up — if you don't have
|
||||
**gevent** installed, this will fail.::
|
||||
|
||||
from requests import async
|
||||
|
||||
The ``async`` module has the exact same api as ``requests``, except it
|
||||
doesn't send the request immediately. Instead, it returns the ``Request``
|
||||
object.
|
||||
|
||||
We can build a list of ``Request`` objects easily::
|
||||
|
||||
urls = [
|
||||
'http://python-requests.org',
|
||||
'http://httpbin.org',
|
||||
'http://python-guide.org',
|
||||
'http://kennethreitz.com'
|
||||
]
|
||||
|
||||
rs = [async.get(u) for u in urls]
|
||||
|
||||
Now we have a list of ``Request`` objects, ready to be sent. We could
|
||||
send them one at a time with ``Request.send()``, but that would take a while.
|
||||
Instead, we'll send them all at the same time with ``async.map()``::
|
||||
|
||||
>>> async.map(rs)
|
||||
[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]
|
||||
|
||||
|
||||
Event Hooks
|
||||
-----------
|
||||
|
||||
Requests has a hook system that you can use to manipulate portions of
|
||||
the request process, or signal event handling.
|
||||
|
||||
Available hooks:
|
||||
|
||||
``args``:
|
||||
A dictionary of the arguments being sent to Request().
|
||||
|
||||
``pre_request``:
|
||||
The Request object, directly before being sent.
|
||||
|
||||
``post_request``:
|
||||
The Request object, directly after being sent.
|
||||
|
||||
``response``:
|
||||
The response generated from a Request.
|
||||
|
||||
|
||||
You can assign a hook function on a per-request basis by passing a
|
||||
``{hook_name: callback_function}`` dictionary to the ``hooks`` request
|
||||
parameter::
|
||||
|
||||
hooks=dict(args=print_url)
|
||||
|
||||
That ``callback_function`` will receive a chunk of data as its first
|
||||
argument.
|
||||
|
||||
::
|
||||
|
||||
def print_url(args):
|
||||
print args['url']
|
||||
|
||||
If an error occurs while executing your callback, a warning is given.
|
||||
|
||||
If the callback function returns a value, it is assumed that it is to
|
||||
replace the data that was passed in. If the function doesn't return
|
||||
anything, nothing else is effected.
|
||||
|
||||
Let's print some request method arguments at runtime::
|
||||
|
||||
>>> requests.get('http://httpbin', hooks=dict(args=print_url))
|
||||
http://httpbin
|
||||
<Response [200]>
|
||||
|
||||
Let's hijack some arguments this time with a new callback::
|
||||
|
||||
def hack_headers(args):
|
||||
if not args[headers]:
|
||||
args['headers'] = dict()
|
||||
|
||||
args['headers'].update({'X-Testing': 'True'})
|
||||
|
||||
return args
|
||||
|
||||
hooks = dict(args=hack_headers)
|
||||
headers = dict(yo=dawg)
|
||||
|
||||
And give it a try::
|
||||
|
||||
>>> requests.get('http://httpbin/headers', hooks=hooks, headers=headers)
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "",
|
||||
"Accept-Encoding": "gzip",
|
||||
"Yo": "dawg",
|
||||
"X-Forwarded-For": "::ffff:24.127.96.129",
|
||||
"Connection": "close",
|
||||
"User-Agent": "python-requests.org",
|
||||
"Host": "httpbin.org",
|
||||
"X-Testing": "True",
|
||||
"X-Forwarded-Protocol": "",
|
||||
"Content-Type": ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Verbose Logging
|
||||
---------------
|
||||
|
||||
If you want to get a good look at what HTTP requests are being sent
|
||||
by your application, you can turn on verbose logging.
|
||||
|
||||
To do so, just configure Requests with a stream to write to::
|
||||
|
||||
>>> requests.settings.verbose = sys.stderr
|
||||
>>> requests.get('http://httpbin.org/headers')
|
||||
2011-08-17T03:04:23.380175 GET http://httpbin.org/headers
|
||||
<Response [200]>
|
||||
56
docs/user/install.rst
Normal file
56
docs/user/install.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
.. _install:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
This part of the documentation covers the installation of Requests.
|
||||
The first step to using any software package is getting it properly installed.
|
||||
|
||||
|
||||
Distribute & Pip
|
||||
----------------
|
||||
|
||||
Installing requests is simple with `pip <http://www.pip-installer.org/>`_::
|
||||
|
||||
$ pip install requests
|
||||
|
||||
or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
|
||||
|
||||
$ easy_install requests
|
||||
|
||||
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/index.html#pip-compared-to-easy-install>`_.
|
||||
|
||||
|
||||
|
||||
Cheeseshop Mirror
|
||||
-----------------
|
||||
|
||||
If the Cheeseshop is down, you can also install Requests from Kenneth Reitz's
|
||||
personal `Cheeseshop mirror <http://pip.kreitz.co/>`_::
|
||||
|
||||
$ pip install -i http://pip.kreitz.co/simple requests
|
||||
|
||||
|
||||
Get the Code
|
||||
------------
|
||||
|
||||
Requests is actively developed on GitHub, where the code is
|
||||
`always available <https://github.com/kennethreitz/requests>`_.
|
||||
|
||||
You can either clone the public repository::
|
||||
|
||||
git clone git://github.com/kennethreitz/requests.git
|
||||
|
||||
Download the `tarball <https://github.com/kennethreitz/requests/tarball/master>`_::
|
||||
|
||||
$ curl -O https://github.com/kennethreitz/requests/tarball/master
|
||||
|
||||
Or, download the `zipball <https://github.com/kennethreitz/requests/zipball/master>`_::
|
||||
|
||||
$ curl -O https://github.com/kennethreitz/requests/zipball/master
|
||||
|
||||
|
||||
Once you have a copy of the source, you can embed it in your Python package,
|
||||
or install it into your site-packages easily::
|
||||
|
||||
$ python setup.py install
|
||||
51
docs/user/intro.rst
Normal file
51
docs/user/intro.rst
Normal file
@@ -0,0 +1,51 @@
|
||||
.. _introduction:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Requests was developed with a few :pep:`20` idioms in mind.
|
||||
|
||||
|
||||
#. Beautiful is better than ugly.
|
||||
#. Explicit is better than implicit.
|
||||
#. Simple is better than complex.
|
||||
#. Complex is better than complicated.
|
||||
#. Readability counts.
|
||||
|
||||
All contributions to Requests should keep these important rules in mind.
|
||||
|
||||
|
||||
ISC License
|
||||
-----------
|
||||
|
||||
A large number of open source projects you find today are `GPL Licensed`_.
|
||||
While the GPL has its time and place, it should most certainly not be your
|
||||
go-to license for your next open source project.
|
||||
|
||||
A project that is released as GPL cannot be used in any commercial product
|
||||
without the product itself also being offered as open source.
|
||||
|
||||
The MIT, BSD, ISC, and Apache2 licenses are great alternatives to the GPL
|
||||
that allow your open-source software to be used freely in proprietary,
|
||||
closed-source software.
|
||||
|
||||
Requests is released under terms of `The ISC License`_.
|
||||
|
||||
.. _`GPL Licensed`: http://www.opensource.org/licenses/gpl-license.php
|
||||
.. _`The ISC License`: http://www.opensource.org/licenses/isc-license
|
||||
|
||||
|
||||
Requests License
|
||||
----------------
|
||||
|
||||
Copyright (c) 2011, Kenneth Reitz
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
|
||||
138
docs/user/quickstart.rst
Normal file
138
docs/user/quickstart.rst
Normal file
@@ -0,0 +1,138 @@
|
||||
.. _quickstart:
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
.. module:: requests.models
|
||||
|
||||
Eager to get started? This page gives a good introduction in how to get started
|
||||
with Requests. This assumes you already have Requests installed. If you do not,
|
||||
head over to the :ref:`Installation <install>` section.
|
||||
|
||||
First, make sure that:
|
||||
|
||||
* Requests is :ref:`installed <install>`
|
||||
* Requests is :ref:`up-to-date <updates>`
|
||||
|
||||
|
||||
Lets gets started with some simple use cases and examples.
|
||||
|
||||
|
||||
Make a GET Request
|
||||
------------------
|
||||
|
||||
Making a standard request with Requests is very simple.
|
||||
|
||||
Let's get GitHub's public timeline ::
|
||||
|
||||
r = requests.get('https://github.com/timeline.json')
|
||||
|
||||
Now, we have a :class:`Response` object called ``r``. We can get all the
|
||||
information we need from this.
|
||||
|
||||
|
||||
Response Content
|
||||
----------------
|
||||
|
||||
We can read the content of the server's response::
|
||||
|
||||
>>> r.content
|
||||
'[{"repository":{"open_issues":0,"url":"https://github.com/...
|
||||
|
||||
|
||||
Response Status Codes
|
||||
---------------------
|
||||
|
||||
We can check the response status code::
|
||||
|
||||
>>> r.status_code
|
||||
200
|
||||
|
||||
Requests also comes with a built-in status code lookup object for easy
|
||||
reference::
|
||||
|
||||
>>> r.status_code == requests.codes.ok
|
||||
True
|
||||
|
||||
If we made a bad request, we can raise it with
|
||||
:class:`Response.raise_for_status()`::
|
||||
|
||||
>>> _r = requests.get('http://httpbin.org/status/404')
|
||||
>>> _r.status_code
|
||||
404
|
||||
|
||||
>>> _r.raise_for_status()
|
||||
Traceback (most recent call last):
|
||||
File "requests/models.py", line 394, in raise_for_status
|
||||
raise self.error
|
||||
urllib2.HTTPError: HTTP Error 404: NOT FOUND
|
||||
|
||||
But, since our ``status_code`` was ``200``, when we call it::
|
||||
|
||||
>>> r.raise_for_status()
|
||||
None
|
||||
|
||||
All is well.
|
||||
|
||||
|
||||
Response Headers
|
||||
----------------
|
||||
|
||||
We can view the server's response headers with a simple Python dictionary
|
||||
interface::
|
||||
|
||||
>>> r.headers
|
||||
{
|
||||
'status': '200 OK',
|
||||
'content-encoding': 'gzip',
|
||||
'transfer-encoding': 'chunked',
|
||||
'connection': 'close',
|
||||
'server': 'nginx/1.0.4',
|
||||
'x-runtime': '148ms',
|
||||
'etag': '"e1ca502697e5c9317743dc078f67693f"',
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
}
|
||||
|
||||
The dictionary is special, though: it's made just for HTTP headers. According to
|
||||
`RFC 2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>`_, HTTP
|
||||
Headers are case-insensitive.
|
||||
|
||||
So, we can access the headers using any capitalization we want::
|
||||
|
||||
>>> r.headers['Content-Type']
|
||||
'application/json; charset=utf-8'
|
||||
|
||||
>>> r.headers.get('content-type')
|
||||
'application/json; charset=utf-8'
|
||||
|
||||
If a header doesn't exist in the Response, its value defaults to ``None``::
|
||||
|
||||
>>> r.headers['X-Random']
|
||||
None
|
||||
|
||||
|
||||
Cookies
|
||||
-------
|
||||
|
||||
If a response contains some Cookies, you can get quick access to them::
|
||||
|
||||
>>> url = 'http://httpbin.org/cookies/set/requests-is/awesome'
|
||||
>>> r = requests.get(url)
|
||||
|
||||
>>> print r.cookies
|
||||
{'requests-is': 'awesome'}
|
||||
|
||||
The underlying CookieJar is also available for more advanced handling::
|
||||
|
||||
>>> r.request.cookiejar
|
||||
<cookielib.CookieJar>
|
||||
|
||||
To send your own cookies to the server, you can use the ``cookies``
|
||||
parameter::
|
||||
|
||||
>>> url = 'http://httpbin.org/cookies'
|
||||
>>> cookies = dict(cookies_are='working')
|
||||
|
||||
>>> r = requests.get(url, cookies=cookies)
|
||||
>>> r.content
|
||||
'{"cookies": {"cookies_are": "working"}}'
|
||||
2217
ext/requests-logo.ai
Normal file
2217
ext/requests-logo.ai
Normal file
File diff suppressed because one or more lines are too long
@@ -1,31 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import inspect
|
||||
|
||||
import packages
|
||||
from core import *
|
||||
|
||||
from core import __version__
|
||||
|
||||
timeout = None
|
||||
|
||||
class settings:
|
||||
"""Context manager for settings."""
|
||||
|
||||
cache = {}
|
||||
|
||||
def __init__(self, timeout):
|
||||
self.module = inspect.getmodule(self)
|
||||
|
||||
# Cache settings
|
||||
self.cache['timeout'] = self.module.timeout
|
||||
|
||||
self.module.timeout = timeout
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
# Restore settings
|
||||
for key in self.cache:
|
||||
setattr(self.module, key, self.cache[key])
|
||||
|
||||
145
requests/api.py
145
requests/api.py
@@ -11,105 +11,182 @@ This module impliments the Requests API.
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
from .models import Request, Response, AuthManager, AuthObject, auth_manager
|
||||
import config
|
||||
from .models import Request, Response, AuthObject
|
||||
from .status_codes import codes
|
||||
from .hooks import dispatch_hook
|
||||
from .utils import cookiejar_from_dict
|
||||
|
||||
|
||||
__all__ = ('request', 'get', 'head', 'post', 'put', 'delete')
|
||||
__all__ = ('request', 'get', 'head', 'post', 'patch', 'put', 'delete')
|
||||
|
||||
def request(method, url,
|
||||
params=None, data=None, headers=None, cookies=None, files=None, auth=None,
|
||||
timeout=None, allow_redirects=False, proxies=None, hooks=None, return_response=True):
|
||||
|
||||
|
||||
def request(method, url, **kwargs):
|
||||
"""Sends a `method` request. Returns :class:`Response` object.
|
||||
"""Constructs and sends a :class:`Request <models.Request>`.
|
||||
Returns :class:`Response <models.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 params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of 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 cookies: (optional) Dict or 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.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
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)),
|
||||
timeout=kwargs.pop('timeout', requests.timeout))
|
||||
if cookies is None:
|
||||
cookies = {}
|
||||
|
||||
cookies = cookiejar_from_dict(cookies)
|
||||
|
||||
args = dict(
|
||||
method = method,
|
||||
url = url,
|
||||
data = data,
|
||||
params = params,
|
||||
headers = headers,
|
||||
cookiejar = cookies,
|
||||
files = files,
|
||||
auth = auth,
|
||||
hooks = hooks,
|
||||
timeout = timeout or config.settings.timeout,
|
||||
allow_redirects = allow_redirects,
|
||||
proxies = proxies or config.settings.proxies,
|
||||
)
|
||||
|
||||
# Arguments manipulation hook.
|
||||
args = dispatch_hook('args', hooks, args)
|
||||
|
||||
r = Request(**args)
|
||||
|
||||
# Pre-request hook.
|
||||
r = dispatch_hook('pre_request', hooks, r)
|
||||
|
||||
# Don't send if asked nicely.
|
||||
if not return_response:
|
||||
return r
|
||||
|
||||
# Send the HTTP Request.
|
||||
r.send()
|
||||
|
||||
# Post-request hook.
|
||||
r = dispatch_hook('post_request', hooks, r)
|
||||
|
||||
# Response manipulation hook.
|
||||
r.response = dispatch_hook('response', hooks, r.response)
|
||||
|
||||
return r.response
|
||||
|
||||
|
||||
def get(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
|
||||
def get(url, **kwargs):
|
||||
|
||||
"""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`.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for 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 cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to False to disable redirect following.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('GET', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
return request('GET', url, **kwargs)
|
||||
|
||||
|
||||
def head(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
|
||||
def head(url, **kwargs):
|
||||
|
||||
"""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`.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for 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 cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to False to disable redirect following.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('HEAD', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
return request('HEAD', url, **kwargs)
|
||||
|
||||
|
||||
def post(url, data={}, headers={}, files=None, cookies=None, auth=None, **kwargs):
|
||||
def post(url, data='', **kwargs):
|
||||
|
||||
"""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 or bytes to send in the body of 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 cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('POST', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
|
||||
return request('POST', url, data=data, **kwargs)
|
||||
|
||||
|
||||
def put(url, data='', headers={}, files={}, cookies=None, auth=None, **kwargs):
|
||||
def put(url, data='', **kwargs):
|
||||
"""Sends a PUT request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param params: (optional) Bytes of PUT Data to send with the :class:`Request`.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of 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 cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('PUT', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
|
||||
return request('PUT', url, data=data, **kwargs)
|
||||
|
||||
|
||||
def delete(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
|
||||
def patch(url, data='', **kwargs):
|
||||
"""Sends a PATCH request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of 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) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('PATCH', url, **kwargs)
|
||||
|
||||
|
||||
def delete(url, **kwargs):
|
||||
|
||||
"""Sends a DELETE request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param params: (optional) Dictionary of DELETE Parameters to send with the :class:`Request`.
|
||||
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for 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 cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
"""
|
||||
|
||||
return request('DELETE', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
|
||||
return request('DELETE', url, **kwargs)
|
||||
|
||||
@@ -1,48 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.async
|
||||
~~~~~~~~~~~~~~
|
||||
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.
|
||||
This module contains an asynchronous replica of ``requests.api``, powered
|
||||
by gevent. All API methods return a ``Request`` instance (as opposed to
|
||||
``Response``). A list of requests can be sent with ``map()``.
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
from urllib2 import HTTPError
|
||||
|
||||
|
||||
try:
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
import gevent
|
||||
from gevent import monkey as curious_george
|
||||
except ImportError:
|
||||
pass
|
||||
raise RuntimeError('Gevent is required for requests.async.')
|
||||
|
||||
if not 'eventlet' in locals():
|
||||
try:
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
except ImportError:
|
||||
pass
|
||||
# Monkey-patch.
|
||||
curious_george.patch_all(thread=False)
|
||||
|
||||
from . import api
|
||||
from .hooks import dispatch_hook
|
||||
|
||||
|
||||
if not 'eventlet' in locals():
|
||||
raise ImportError('No Async adaptations of urllib2 found!')
|
||||
__all__ = (
|
||||
'map',
|
||||
'get', 'head', 'post', 'put', 'patch', 'delete', 'request'
|
||||
)
|
||||
|
||||
|
||||
def _patched(f):
|
||||
"""Patches a given API function to not send."""
|
||||
|
||||
def wrapped(*args, **kwargs):
|
||||
return f(*args, return_response=False, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def _send(r, pools=None):
|
||||
"""Sends a given Request object."""
|
||||
|
||||
if pools:
|
||||
r._pools = pools
|
||||
|
||||
r.send()
|
||||
|
||||
# Post-request hook.
|
||||
r = dispatch_hook('post_request', r.hooks, r)
|
||||
|
||||
# Response manipulation hook.
|
||||
r.response = dispatch_hook('response', r.hooks, r.response)
|
||||
|
||||
return r.response
|
||||
|
||||
|
||||
# Patched requests.api functions.
|
||||
get = _patched(api.get)
|
||||
head = _patched(api.head)
|
||||
post = _patched(api.post)
|
||||
put = _patched(api.put)
|
||||
patch = _patched(api.patch)
|
||||
delete = _patched(api.delete)
|
||||
request = _patched(api.request)
|
||||
|
||||
|
||||
def map(requests, prefetch=True):
|
||||
"""Concurrently converts a list of Requests to Responses.
|
||||
|
||||
:param requests: a collection of Request objects.
|
||||
:param prefetch: If False, the content will not be downloaded immediately.
|
||||
"""
|
||||
|
||||
jobs = [gevent.spawn(_send, r) for r in requests]
|
||||
gevent.joinall(jobs)
|
||||
|
||||
if prefetch:
|
||||
[r.response.content for r in requests]
|
||||
|
||||
return [r.response for r in requests]
|
||||
|
||||
|
||||
from .core import *
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
|
||||
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
|
||||
'URLRequired', 'InvalidMethod', 'HTTPError'
|
||||
]
|
||||
|
||||
67
requests/config.py
Normal file
67
requests/config.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.config
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This module provides the Requests settings feature set.
|
||||
|
||||
"""
|
||||
|
||||
class Settings(object):
|
||||
_singleton = {}
|
||||
|
||||
# attributes with defaults
|
||||
__attrs__ = []
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Settings, self).__init__()
|
||||
|
||||
self.__dict__ = self._singleton
|
||||
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
# new instance of class to call
|
||||
r = self.__class__()
|
||||
|
||||
# cache previous settings for __exit__
|
||||
r.__cache = self.__dict__.copy()
|
||||
map(self.__cache.setdefault, self.__attrs__)
|
||||
|
||||
# set new settings
|
||||
self.__dict__.update(*args, **kwargs)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
|
||||
def __exit__(self, *args):
|
||||
|
||||
# restore cached copy
|
||||
self.__dict__.update(self.__cache.copy())
|
||||
del self.__cache
|
||||
|
||||
|
||||
def __getattribute__(self, key):
|
||||
if key in object.__getattribute__(self, '__attrs__'):
|
||||
try:
|
||||
return object.__getattribute__(self, key)
|
||||
except AttributeError:
|
||||
return None
|
||||
return object.__getattribute__(self, key)
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
settings.base_headers = {'User-Agent': 'python-requests.org'}
|
||||
settings.accept_gzip = True
|
||||
settings.proxies = None
|
||||
settings.verbose = None
|
||||
settings.timeout = None
|
||||
settings.max_redirects = 30
|
||||
|
||||
#: Use socket.setdefaulttimeout() as fallback?
|
||||
settings.timeout_fallback = True
|
||||
@@ -12,12 +12,16 @@ This module implements the main Requests system.
|
||||
"""
|
||||
|
||||
__title__ = 'requests'
|
||||
__version__ = '0.3.4'
|
||||
__build__ = 0x000304
|
||||
__version__ = '0.6.3'
|
||||
__build__ = 0x000603
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = 'Copyright 2011 Kenneth Reitz'
|
||||
|
||||
|
||||
from .models import HTTPError, auth_manager
|
||||
from .api import *
|
||||
from models import HTTPError
|
||||
from api import *
|
||||
from exceptions import *
|
||||
from sessions import session
|
||||
from status_codes import codes
|
||||
from config import settings
|
||||
26
requests/exceptions.py
Normal file
26
requests/exceptions.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.exceptions
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
"""
|
||||
|
||||
class RequestException(Exception):
|
||||
"""There was an ambiguous exception that occured while handling your
|
||||
request."""
|
||||
|
||||
class AuthenticationError(RequestException):
|
||||
"""The authentication credentials provided were invalid."""
|
||||
|
||||
class Timeout(RequestException):
|
||||
"""The request timed out."""
|
||||
|
||||
class URLRequired(RequestException):
|
||||
"""A valid URL is required to make a request."""
|
||||
|
||||
class InvalidMethod(RequestException):
|
||||
"""An inappropriate method was attempted."""
|
||||
|
||||
class TooManyRedirects(RequestException):
|
||||
"""Too many redirects."""
|
||||
40
requests/hooks.py
Normal file
40
requests/hooks.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.hooks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module provides the capabilities for the Requests hooks system.
|
||||
|
||||
Available hooks:
|
||||
|
||||
``args``:
|
||||
A dictionary of the arguments being sent to Request().
|
||||
|
||||
``pre_request``:
|
||||
The Request object, directly before being sent.
|
||||
|
||||
``post_request``:
|
||||
The Request object, directly after being sent.
|
||||
|
||||
``response``:
|
||||
The response generated from a Request.
|
||||
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
|
||||
def dispatch_hook(key, hooks, hook_data):
|
||||
"""Dipatches a hook dictionary on a given peice of data."""
|
||||
|
||||
hooks = hooks or dict()
|
||||
|
||||
if key in hooks:
|
||||
try:
|
||||
return hooks.get(key).__call__(hook_data) or hook_data
|
||||
|
||||
except Exception, why:
|
||||
warnings.warn(str(why))
|
||||
|
||||
return hook_data
|
||||
@@ -1,83 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.system
|
||||
requests.models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
import urllib2
|
||||
import socket
|
||||
import zlib
|
||||
|
||||
from urllib2 import HTTPError
|
||||
from urlparse import urlparse
|
||||
|
||||
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPDigestAuthHandler
|
||||
from urlparse import urlparse, urlunparse, urljoin
|
||||
from datetime import datetime
|
||||
|
||||
from .config import settings
|
||||
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPForcedBasicAuthHandler, HTTPDigestAuthHandler, HTTPRedirectHandler
|
||||
from .structures import CaseInsensitiveDict
|
||||
from .packages.poster.encode import multipart_encode
|
||||
from .packages.poster.streaminghttp import register_openers, get_handlers
|
||||
from .utils import dict_from_cookiejar
|
||||
from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects
|
||||
from .status_codes import codes
|
||||
|
||||
|
||||
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
|
||||
|
||||
|
||||
|
||||
class Request(object):
|
||||
"""The :class:`Request` object. It carries out all functionality of
|
||||
"""The :class:`Request <models.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, data=dict(),
|
||||
params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False,
|
||||
allow_redirects=False, proxies=None, hooks=None):
|
||||
|
||||
def __init__(self, url=None, headers=dict(), files=None, method=None,
|
||||
data=dict(), auth=None, cookiejar=None, timeout=None):
|
||||
|
||||
socket.setdefaulttimeout(timeout)
|
||||
#: Float describ the timeout of the request.
|
||||
# (Use socket.setdefaulttimeout() as fallback)
|
||||
self.timeout = timeout
|
||||
|
||||
#: Request URL.
|
||||
self.url = url
|
||||
|
||||
#: Dictonary of HTTP Headers to attach to the :class:`Request <models.Request>`.
|
||||
self.headers = headers
|
||||
|
||||
#: Dictionary of files to multipart upload (``{filename: content}``).
|
||||
self.files = files
|
||||
|
||||
#: HTTP Method to use. Available: GET, HEAD, PUT, POST, DELETE.
|
||||
self.method = method
|
||||
self.data = {}
|
||||
|
||||
# self.data = {}
|
||||
if hasattr(data, 'items'):
|
||||
for (k, v) in data.items():
|
||||
self.data.update({
|
||||
k.encode('utf-8') if isinstance(k, unicode) else k:
|
||||
v.encode('utf-8') if isinstance(v, unicode) else v
|
||||
})
|
||||
#: Dictionary or byte of request body data to attach to the
|
||||
#: :class:`Request <models.Request>`.
|
||||
self.data = None
|
||||
|
||||
# url encode data if it's a dict
|
||||
if hasattr(data, 'items'):
|
||||
self._enc_data = urllib.urlencode(self.data)
|
||||
else:
|
||||
self._enc_data = data
|
||||
#: Dictionary or byte of querystring data to attach to the
|
||||
#: :class:`Request <models.Request>`.
|
||||
self.params = None
|
||||
|
||||
#: True if :class:`Request <models.Request>` is part of a redirect chain (disables history
|
||||
#: and HTTPError storage).
|
||||
self.redirect = redirect
|
||||
|
||||
#: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
|
||||
self.allow_redirects = allow_redirects
|
||||
|
||||
# Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
|
||||
self.proxies = proxies
|
||||
|
||||
self.data, self._enc_data = self._encode_params(data)
|
||||
self.params, self._enc_params = self._encode_params(params)
|
||||
|
||||
#: :class:`Response <models.Response>` instance, containing
|
||||
#: content and metadata of HTTP Response, once :attr:`sent <send>`.
|
||||
self.response = Response()
|
||||
|
||||
if isinstance(auth, (list, tuple)):
|
||||
auth = AuthObject(*auth)
|
||||
if not auth:
|
||||
auth = auth_manager.get_auth(self.url)
|
||||
|
||||
#: :class:`AuthObject` to attach to :class:`Request <models.Request>`.
|
||||
self.auth = auth
|
||||
|
||||
#: CookieJar to attach to :class:`Request <models.Request>`.
|
||||
self.cookiejar = cookiejar
|
||||
|
||||
#: True if Request has been sent.
|
||||
self.sent = False
|
||||
|
||||
#: Event-handling hooks.
|
||||
self.hooks = hooks
|
||||
|
||||
# Header manipulation and defaults.
|
||||
|
||||
if settings.accept_gzip:
|
||||
settings.base_headers.update({'Accept-Encoding': 'gzip'})
|
||||
|
||||
if headers:
|
||||
headers = CaseInsensitiveDict(self.headers)
|
||||
else:
|
||||
headers = CaseInsensitiveDict()
|
||||
|
||||
for (k, v) in settings.base_headers.items():
|
||||
if k not in headers:
|
||||
headers[k] = v
|
||||
|
||||
self.headers = headers
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
@@ -95,16 +135,24 @@ class Request(object):
|
||||
|
||||
if self.auth:
|
||||
if not isinstance(self.auth.handler, (urllib2.AbstractBasicAuthHandler, urllib2.AbstractDigestAuthHandler)):
|
||||
# TODO: REMOVE THIS COMPLETELY
|
||||
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 self.proxies:
|
||||
_handlers.append(urllib2.ProxyHandler(self.proxies))
|
||||
|
||||
_handlers.append(HTTPRedirectHandler)
|
||||
|
||||
if not _handlers:
|
||||
return urllib2.urlopen
|
||||
|
||||
_handlers.extend(get_handlers())
|
||||
if self.data or self.files:
|
||||
_handlers.extend(get_handlers())
|
||||
|
||||
opener = urllib2.build_opener(*_handlers)
|
||||
|
||||
if self.headers:
|
||||
@@ -118,40 +166,128 @@ class Request(object):
|
||||
|
||||
return opener.open
|
||||
|
||||
def _build_response(self, resp):
|
||||
"""Build internal Response object from given response."""
|
||||
if isinstance(resp, HTTPError):
|
||||
# print resp.__dict__
|
||||
pass
|
||||
|
||||
self.response.status_code = getattr(resp, 'code', None)
|
||||
def _build_response(self, resp, is_error=False):
|
||||
"""Build internal :class:`Response <models.Response>` object from given response."""
|
||||
|
||||
try:
|
||||
self.response.headers = getattr(resp.info(), 'dict', None)
|
||||
self.response.content = resp.read()
|
||||
except AttributeError, why:
|
||||
pass
|
||||
def build(resp):
|
||||
|
||||
response = Response()
|
||||
response.status_code = getattr(resp, 'code', None)
|
||||
|
||||
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:
|
||||
response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None))
|
||||
response.read = resp.read
|
||||
response._resp = resp
|
||||
response._close = resp.close
|
||||
|
||||
if self.cookiejar:
|
||||
|
||||
response.cookies = dict_from_cookiejar(self.cookiejar)
|
||||
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
self.response.url = getattr(resp, 'url', None)
|
||||
if is_error:
|
||||
response.error = resp
|
||||
|
||||
response.url = getattr(resp, 'url', None)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
history = []
|
||||
|
||||
r = build(resp)
|
||||
|
||||
if r.status_code in REDIRECT_STATI and not self.redirect:
|
||||
|
||||
while (
|
||||
('location' in r.headers) and
|
||||
((r.status_code is codes.see_other) or (self.allow_redirects))
|
||||
):
|
||||
|
||||
r.close()
|
||||
|
||||
if not len(history) < settings.max_redirects:
|
||||
raise TooManyRedirects()
|
||||
|
||||
history.append(r)
|
||||
|
||||
url = r.headers['location']
|
||||
|
||||
# Handle redirection without scheme (see: RFC 1808 Section 4)
|
||||
if url.startswith('//'):
|
||||
parsed_rurl = urlparse(r.url)
|
||||
url = '%s:%s' % (parsed_rurl.scheme, url)
|
||||
|
||||
# Facilitate non-RFC2616-compliant 'location' headers
|
||||
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
|
||||
if not urlparse(url).netloc:
|
||||
url = urljoin(r.url, urllib.quote(urllib.unquote(url)))
|
||||
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
|
||||
if r.status_code is codes.see_other:
|
||||
method = 'GET'
|
||||
else:
|
||||
method = self.method
|
||||
|
||||
request = Request(
|
||||
url, self.headers, self.files, method,
|
||||
self.data, self.params, self.auth, self.cookiejar,
|
||||
redirect=True
|
||||
)
|
||||
request.send()
|
||||
r = request.response
|
||||
|
||||
r.history = history
|
||||
|
||||
self.response = r
|
||||
self.response.request = self
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _build_url(url, data=None):
|
||||
"""Build URLs."""
|
||||
def _encode_params(data):
|
||||
"""Encode parameters in a piece of data.
|
||||
|
||||
if urlparse(url).query:
|
||||
return '%s&%s' % (url, data)
|
||||
If the data supplied is a dictionary, encodes each parameter in it, and
|
||||
returns a list of tuples containing the encoded parameters, and a urlencoded
|
||||
version of that.
|
||||
|
||||
Otherwise, assumes the data is already encoded appropriately, and
|
||||
returns it twice.
|
||||
|
||||
"""
|
||||
if hasattr(data, 'items'):
|
||||
result = []
|
||||
for k, vs in data.items():
|
||||
for v in isinstance(vs, list) and vs or [vs]:
|
||||
result.append((k.encode('utf-8') if isinstance(k, unicode) else k,
|
||||
v.encode('utf-8') if isinstance(v, unicode) else v))
|
||||
return result, urllib.urlencode(result, doseq=True)
|
||||
else:
|
||||
if data:
|
||||
return '%s?%s' % (url, data)
|
||||
return data, data
|
||||
|
||||
|
||||
def _build_url(self):
|
||||
"""Build the actual URL to use."""
|
||||
|
||||
# Support for unicode domain names and paths.
|
||||
scheme, netloc, path, params, query, fragment = urlparse(self.url)
|
||||
netloc = netloc.encode('idna')
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode('utf-8')
|
||||
path = urllib.quote(urllib.unquote(path))
|
||||
self.url = str(urlunparse([ scheme, netloc, path, params, query, fragment ]))
|
||||
|
||||
if self._enc_params:
|
||||
if urlparse(self.url).query:
|
||||
return '%s&%s' % (self.url, self._enc_params)
|
||||
else:
|
||||
return url
|
||||
return '%s?%s' % (self.url, self._enc_params)
|
||||
else:
|
||||
return self.url
|
||||
|
||||
|
||||
def send(self, anyway=False):
|
||||
@@ -164,11 +300,20 @@ class Request(object):
|
||||
:param anyway: If True, request will be sent, even if it has
|
||||
already been sent.
|
||||
"""
|
||||
|
||||
self._checks()
|
||||
success = False
|
||||
|
||||
# Logging
|
||||
if settings.verbose:
|
||||
settings.verbose.write('%s %s %s\n' % (
|
||||
datetime.now().isoformat(), self.method, self.url
|
||||
))
|
||||
|
||||
|
||||
url = self._build_url()
|
||||
if self.method in ('GET', 'HEAD', 'DELETE'):
|
||||
req = _Request(self._build_url(self.url, self._enc_data), method=self.method)
|
||||
req = _Request(url, method=self.method)
|
||||
else:
|
||||
|
||||
if self.files:
|
||||
@@ -178,58 +323,95 @@ class Request(object):
|
||||
self.files.update(self.data)
|
||||
|
||||
datagen, headers = multipart_encode(self.files)
|
||||
req = _Request(self.url, data=datagen, headers=headers, method=self.method)
|
||||
req = _Request(url, data=datagen, headers=headers, method=self.method)
|
||||
|
||||
else:
|
||||
req = _Request(self.url, data=self._enc_data, method=self.method)
|
||||
req = _Request(url, data=self._enc_data, method=self.method)
|
||||
|
||||
if self.headers:
|
||||
req.headers.update(self.headers)
|
||||
for k,v in self.headers.iteritems():
|
||||
req.add_header(k, v)
|
||||
|
||||
if not self.sent or anyway:
|
||||
|
||||
try:
|
||||
opener = self._get_opener()
|
||||
resp = opener(req)
|
||||
try:
|
||||
|
||||
resp = opener(req, timeout=self.timeout)
|
||||
|
||||
except TypeError, err:
|
||||
# timeout argument is new since Python v2.6
|
||||
if not 'timeout' in str(err):
|
||||
raise
|
||||
|
||||
if settings.timeout_fallback:
|
||||
# fall-back and use global socket timeout (This is not thread-safe!)
|
||||
old_timeout = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(self.timeout)
|
||||
|
||||
resp = opener(req)
|
||||
|
||||
if settings.timeout_fallback:
|
||||
# restore gobal timeout
|
||||
socket.setdefaulttimeout(old_timeout)
|
||||
|
||||
if self.cookiejar is not None:
|
||||
self.cookiejar.extract_cookies(resp, req)
|
||||
|
||||
except urllib2.HTTPError, why:
|
||||
self._build_response(why)
|
||||
self.response.error = why
|
||||
except (urllib2.HTTPError, urllib2.URLError), why:
|
||||
if hasattr(why, 'reason'):
|
||||
if isinstance(why.reason, socket.timeout):
|
||||
why = Timeout(why)
|
||||
elif isinstance(why.reason, socket.error):
|
||||
why = Timeout(why)
|
||||
|
||||
self._build_response(why, is_error=True)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
this class.
|
||||
"""The core :class:`Response <models.Response>` object. All
|
||||
:class:`Request <models.Request>` objects contain a
|
||||
:class:`response <models.Response>` attribute, which is an instance
|
||||
of this class.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.content = None
|
||||
#: Raw content of the response, in bytes.
|
||||
#: If ``content-encoding`` of response was set to ``gzip``, the
|
||||
#: response data will be automatically deflated.
|
||||
self._content = None
|
||||
#: Integer Code of responded HTTP Status.
|
||||
self.status_code = None
|
||||
self.headers = dict()
|
||||
#: Case-insensitive Dictionary of Response Headers.
|
||||
#: For example, ``headers['content-encoding']`` will return the
|
||||
#: value of a ``'Content-Encoding'`` response header.
|
||||
self.headers = CaseInsensitiveDict()
|
||||
#: Final URL location of Response.
|
||||
self.url = None
|
||||
#: True if no :attr:`error` occured.
|
||||
self.ok = False
|
||||
#: Resulting :class:`HTTPError` of request, if one occured.
|
||||
self.error = None
|
||||
self.cached = False
|
||||
#: A list of :class:`Response <models.Response>` objects from
|
||||
#: the history of the Request. Any redirect responses will end
|
||||
#: up here.
|
||||
self.history = []
|
||||
#: The Request that created the Response.
|
||||
self.request = None
|
||||
#: A dictionary of Cookies the server sent back.
|
||||
self.cookies = None
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
@@ -237,22 +419,38 @@ class Response(object):
|
||||
|
||||
|
||||
def __nonzero__(self):
|
||||
"""Returns true if status_code is 'OK'."""
|
||||
"""Returns true if :attr:`status_code` is 'OK'."""
|
||||
return not self.error
|
||||
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Read and returns the full stream when accessing to :attr: `content`"""
|
||||
if name == 'content':
|
||||
if self._content is not None:
|
||||
return self._content
|
||||
self._content = self.read()
|
||||
if self.headers.get('content-encoding', '') == 'gzip':
|
||||
try:
|
||||
self._content = zlib.decompress(self._content, 16+zlib.MAX_WBITS)
|
||||
except zlib.error:
|
||||
pass
|
||||
return self._content
|
||||
else:
|
||||
raise AttributeError
|
||||
|
||||
def raise_for_status(self):
|
||||
"""Raises stored HTTPError if one exists."""
|
||||
"""Raises stored :class:`HTTPError` or :class:`URLError`, if one occured."""
|
||||
if self.error:
|
||||
raise self.error
|
||||
|
||||
def read(self, *args):
|
||||
return self.content
|
||||
|
||||
|
||||
def close(self):
|
||||
if self._resp.fp is not None and hasattr(self._resp.fp, '_sock'):
|
||||
self._resp.fp._sock.recv = None
|
||||
self._close()
|
||||
|
||||
class AuthManager(object):
|
||||
"""Authentication Manager."""
|
||||
"""Requests Authentication Manager."""
|
||||
|
||||
def __new__(cls):
|
||||
singleton = cls.__dict__.get('__singleton__')
|
||||
@@ -324,8 +522,10 @@ class AuthManager(object):
|
||||
|
||||
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]
|
||||
@@ -336,7 +536,9 @@ class AuthManager(object):
|
||||
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,
|
||||
@@ -406,30 +608,18 @@ class AuthObject(object):
|
||||
|
||||
_handlers = {
|
||||
'basic': HTTPBasicAuthHandler,
|
||||
'forced_basic': HTTPForcedBasicAuthHandler,
|
||||
'digest': HTTPDigestAuthHandler,
|
||||
'proxy_basic': urllib2.ProxyBasicAuthHandler,
|
||||
'proxy_digest': urllib2.ProxyDigestAuthHandler
|
||||
}
|
||||
|
||||
def __init__(self, username, password, handler='basic', realm=None):
|
||||
def __init__(self, username, password, handler='forced_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)
|
||||
self.handler = self._handlers.get(handler.lower(), HTTPForcedBasicAuthHandler)
|
||||
else:
|
||||
self.handler = handler
|
||||
|
||||
class RequestException(Exception):
|
||||
"""There was an ambiguous exception that occured while handling your
|
||||
request."""
|
||||
|
||||
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."""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.monkeys
|
||||
@@ -9,7 +9,7 @@ Urllib2 Monkey patches.
|
||||
"""
|
||||
|
||||
import urllib2
|
||||
|
||||
import re
|
||||
|
||||
class Request(urllib2.Request):
|
||||
"""Hidden wrapper around the urllib2.Request object. Allows for manual
|
||||
@@ -27,12 +27,23 @@ class Request(urllib2.Request):
|
||||
return urllib2.Request.get_method(self)
|
||||
|
||||
|
||||
class HTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
"""HTTP Redirect handler."""
|
||||
def http_error_301(self, req, fp, code, msg, headers):
|
||||
pass
|
||||
|
||||
http_error_302 = http_error_303 = http_error_307 = http_error_301
|
||||
|
||||
|
||||
|
||||
class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
|
||||
# from mercurial
|
||||
"""HTTP Basic Auth Handler with authentication loop fixes."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
|
||||
self.retried_req = None
|
||||
self.retried = 0
|
||||
|
||||
|
||||
def reset_retry_count(self):
|
||||
# Python 2.6.5 will call this on 401 or 407 errors and thus loop
|
||||
@@ -40,13 +51,69 @@ class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
|
||||
# http_error_auth_reqed instead.
|
||||
pass
|
||||
|
||||
|
||||
def http_error_auth_reqed(self, auth_header, host, req, headers):
|
||||
# Reset the retry counter once for each request.
|
||||
if req is not self.retried_req:
|
||||
self.retried_req = req
|
||||
self.retried = 0
|
||||
|
||||
return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
|
||||
self, auth_header, host, req, headers)
|
||||
self, auth_header, host, req, headers
|
||||
)
|
||||
|
||||
|
||||
|
||||
class HTTPForcedBasicAuthHandler(HTTPBasicAuthHandler):
|
||||
"""HTTP Basic Auth Handler with forced Authentication."""
|
||||
|
||||
auth_header = 'Authorization'
|
||||
rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
|
||||
'realm=(["\'])(.*?)\\2', re.I)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
def http_error_401(self, req, fp, code, msg, headers):
|
||||
url = req.get_full_url()
|
||||
response = self._http_error_auth_reqed('www-authenticate', url, req, headers)
|
||||
self.reset_retry_count()
|
||||
return response
|
||||
|
||||
http_error_404 = http_error_401
|
||||
|
||||
|
||||
def _http_error_auth_reqed(self, authreq, host, req, headers):
|
||||
|
||||
authreq = headers.get(authreq, None)
|
||||
|
||||
if self.retried > 5:
|
||||
# retry sending the username:password 5 times before failing.
|
||||
raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed",
|
||||
headers, None)
|
||||
else:
|
||||
self.retried += 1
|
||||
|
||||
if authreq:
|
||||
|
||||
mo = self.rx.search(authreq)
|
||||
|
||||
if mo:
|
||||
scheme, quote, realm = mo.groups()
|
||||
|
||||
if scheme.lower() == 'basic':
|
||||
response = self.retry_http_basic_auth(host, req, realm)
|
||||
|
||||
if response and response.code not in (401, 404):
|
||||
self.retried = 0
|
||||
return response
|
||||
else:
|
||||
response = self.retry_http_basic_auth(host, req, 'Realm')
|
||||
|
||||
if response and response.code not in (401, 404):
|
||||
self.retried = 0
|
||||
return response
|
||||
|
||||
|
||||
|
||||
|
||||
84
requests/sessions.py
Normal file
84
requests/sessions.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.session
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This module provides a Session object to manage and persist settings across
|
||||
requests (cookies, auth, proxies).
|
||||
|
||||
"""
|
||||
|
||||
import cookielib
|
||||
|
||||
from . import api
|
||||
from .utils import add_dict_to_cookiejar
|
||||
|
||||
|
||||
|
||||
class Session(object):
|
||||
"""A Requests session."""
|
||||
|
||||
__attrs__ = ['headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks']
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
# Set up a CookieJar to be used by default
|
||||
self.cookies = cookielib.FileCookieJar()
|
||||
|
||||
# Map args from kwargs to instance-local variables
|
||||
map(lambda k, v: (k in self.__attrs__) and setattr(self, k, v),
|
||||
kwargs.iterkeys(), kwargs.itervalues())
|
||||
|
||||
# Map and wrap requests.api methods
|
||||
self._map_api_methods()
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return '<requests-client at 0x%x>' % (id(self))
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
# print args
|
||||
pass
|
||||
|
||||
|
||||
def _map_api_methods(self):
|
||||
"""Reads each available method from requests.api and decorates
|
||||
them with a wrapper, which inserts any instance-local attributes
|
||||
(from __attrs__) that have been set, combining them with **kwargs.
|
||||
"""
|
||||
|
||||
def pass_args(func):
|
||||
def wrapper_func(*args, **kwargs):
|
||||
inst_attrs = dict((k, v) for k, v in self.__dict__.iteritems()
|
||||
if k in self.__attrs__)
|
||||
# Combine instance-local values with kwargs values, with
|
||||
# priority to values in kwargs
|
||||
kwargs = dict(inst_attrs.items() + kwargs.items())
|
||||
|
||||
# If a session request has a cookie_dict, inject the
|
||||
# values into the existing CookieJar instead.
|
||||
if isinstance(kwargs.get('cookies', None), dict):
|
||||
kwargs['cookies'] = add_dict_to_cookiejar(
|
||||
inst_attrs['cookies'], kwargs['cookies']
|
||||
)
|
||||
|
||||
if kwargs.get('headers', None) and inst_attrs.get('headers', None):
|
||||
kwargs['headers'].update(inst_attrs['headers'])
|
||||
|
||||
return func(*args, **kwargs)
|
||||
return wrapper_func
|
||||
|
||||
# Map and decorate each function available in requests.api
|
||||
map(lambda fn: setattr(self, fn, pass_args(getattr(api, fn))),
|
||||
api.__all__)
|
||||
|
||||
|
||||
def session(**kwargs):
|
||||
"""Returns a :class:`Session` for context-managment."""
|
||||
|
||||
return Session(**kwargs)
|
||||
83
requests/status_codes.py
Normal file
83
requests/status_codes.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .structures import LookupDict
|
||||
|
||||
_codes = {
|
||||
|
||||
# Informational.
|
||||
100: ('continue',),
|
||||
101: ('switching_protocols',),
|
||||
102: ('processing',),
|
||||
103: ('checkpoint',),
|
||||
122: ('uri_too_long', 'request_uri_too_long'),
|
||||
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/'),
|
||||
201: ('created',),
|
||||
202: ('accepted',),
|
||||
203: ('non_authoritative_info', 'non_authoritative_information'),
|
||||
204: ('no_content',),
|
||||
205: ('reset_content', 'reset'),
|
||||
206: ('partial_content', 'partial'),
|
||||
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
|
||||
208: ('im_used',),
|
||||
|
||||
# Redirection.
|
||||
300: ('multiple_choices',),
|
||||
301: ('moved_permanently', 'moved', '\\o-'),
|
||||
302: ('found',),
|
||||
303: ('see_other', 'other'),
|
||||
304: ('not_modified',),
|
||||
305: ('use_proxy',),
|
||||
306: ('switch_proxy',),
|
||||
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
|
||||
308: ('resume_incomplete', 'resume'),
|
||||
|
||||
# Client Error.
|
||||
400: ('bad_request', 'bad'),
|
||||
401: ('unauthorized',),
|
||||
402: ('payment_required', 'payment'),
|
||||
403: ('forbidden',),
|
||||
404: ('not_found', '-o-'),
|
||||
405: ('method_not_allowed', 'not_allowed'),
|
||||
406: ('not_acceptable',),
|
||||
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
|
||||
408: ('request_timeout', 'timeout'),
|
||||
409: ('conflict',),
|
||||
410: ('gone',),
|
||||
411: ('length_required',),
|
||||
412: ('precondition_failed', 'precondition'),
|
||||
413: ('request_entity_too_large',),
|
||||
414: ('request_uri_too_large',),
|
||||
415: ('unspported_media_type', 'unspported_media', 'media_type'),
|
||||
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
|
||||
417: ('expectation_failed',),
|
||||
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
|
||||
422: ('unprocessable_entity', 'unprocessable'),
|
||||
423: ('locked',),
|
||||
424: ('failed_depdendency', 'depdendency'),
|
||||
425: ('unordered_collection', 'unordered'),
|
||||
426: ('upgrade_required', 'upgrade'),
|
||||
444: ('no_response', 'none'),
|
||||
449: ('retry_with', 'retry'),
|
||||
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
|
||||
499: ('client_closed_request',),
|
||||
|
||||
# Server Error.
|
||||
500: ('internal_server_error', 'server_error', '/o\\'),
|
||||
501: ('not_implemented',),
|
||||
502: ('bad_gateway',),
|
||||
503: ('service_unavailable', 'unavailable'),
|
||||
504: ('gateway_timeout',),
|
||||
505: ('http_version_not_supported', 'http_version'),
|
||||
506: ('variant_also_negotiates',),
|
||||
507: ('insufficient_storage',),
|
||||
509: ('bandwidth_limit_exceeded', 'bandwidth'),
|
||||
510: ('not_extended',),
|
||||
}
|
||||
|
||||
codes = LookupDict(name='status_codes')
|
||||
|
||||
for (code, titles) in _codes.items():
|
||||
for title in titles:
|
||||
setattr(codes, title, code)
|
||||
if not title.startswith('\\'):
|
||||
setattr(codes, title.upper(), code)
|
||||
@@ -1,360 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# from: werkzeug
|
||||
"""
|
||||
requests.structures
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
class TypeConversionDict(dict):
|
||||
"""Works like a regular dict but the :meth:`get` method can perform
|
||||
type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
|
||||
are subclasses of this class and provide the same feature.
|
||||
"""
|
||||
Datastructures that power Requests.
|
||||
|
||||
def get(self, key, default=None, type=None):
|
||||
"""Return the default value if the requested data doesn't exist.
|
||||
If `type` is provided and is a callable it should convert the value,
|
||||
return it or raise a :exc:`ValueError` if that is not possible. In
|
||||
this case the function will return the default as if the value was not
|
||||
found:
|
||||
"""
|
||||
|
||||
>>> d = TypeConversionDict(foo='42', bar='blub')
|
||||
>>> d.get('foo', type=int)
|
||||
42
|
||||
>>> d.get('bar', -1, type=int)
|
||||
-1
|
||||
class CaseInsensitiveDict(dict):
|
||||
"""Case-insensitive Dictionary
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: The default value to be returned if the key can't
|
||||
be looked up. If not further specified `None` is
|
||||
returned.
|
||||
:param type: A callable that is used to cast the value in the
|
||||
:class:`MultiDict`. If a :exc:`ValueError` is raised
|
||||
by this callable the default value is returned.
|
||||
"""
|
||||
try:
|
||||
rv = self[key]
|
||||
if type is not None:
|
||||
rv = type(rv)
|
||||
except (KeyError, ValueError):
|
||||
rv = default
|
||||
return rv
|
||||
For example, ``headers['content-encoding']`` will return the
|
||||
value of a ``'Content-Encoding'`` response header."""
|
||||
|
||||
@property
|
||||
def lower_keys(self):
|
||||
if not hasattr(self, '_lower_keys') or not self._lower_keys:
|
||||
self._lower_keys = dict((k.lower(), k) for k in self.iterkeys())
|
||||
return self._lower_keys
|
||||
|
||||
|
||||
# from: werkzeug
|
||||
|
||||
class MultiDict(TypeConversionDict):
|
||||
"""A :class:`MultiDict` is a dictionary subclass customized to deal with
|
||||
multiple values for the same key which is for example used by the parsing
|
||||
functions in the wrappers. This is necessary because some HTML form
|
||||
elements pass multiple values for the same key.
|
||||
|
||||
:class:`MultiDict` implements all standard dictionary methods.
|
||||
Internally, it saves all values for a key as a list, but the standard dict
|
||||
access methods will only return the first value for a key. If you want to
|
||||
gain access to the other values, too, you have to use the `list` methods as
|
||||
explained below.
|
||||
|
||||
Basic Usage:
|
||||
|
||||
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
|
||||
>>> d
|
||||
MultiDict([('a', 'b'), ('a', 'c')])
|
||||
>>> d['a']
|
||||
'b'
|
||||
>>> d.getlist('a')
|
||||
['b', 'c']
|
||||
>>> 'a' in d
|
||||
True
|
||||
|
||||
It behaves like a normal dict thus all dict functions will only return the
|
||||
first value when multiple values for one key are found.
|
||||
|
||||
From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
|
||||
subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
|
||||
render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
|
||||
exceptions.
|
||||
|
||||
A :class:`MultiDict` can be constructed from an iterable of
|
||||
``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
|
||||
onwards some keyword parameters.
|
||||
|
||||
:param mapping: the initial value for the :class:`MultiDict`. Either a
|
||||
regular dict, an iterable of ``(key, value)`` tuples
|
||||
or `None`.
|
||||
"""
|
||||
|
||||
# the key error this class raises. Because of circular dependencies
|
||||
# with the http exception module this class is created at the end of
|
||||
# this module.
|
||||
KeyError = None
|
||||
|
||||
def __init__(self, mapping=None):
|
||||
if isinstance(mapping, MultiDict):
|
||||
dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
|
||||
elif isinstance(mapping, dict):
|
||||
tmp = {}
|
||||
for key, value in mapping.iteritems():
|
||||
if isinstance(value, (tuple, list)):
|
||||
value = list(value)
|
||||
else:
|
||||
value = [value]
|
||||
tmp[key] = value
|
||||
dict.__init__(self, tmp)
|
||||
else:
|
||||
tmp = {}
|
||||
for key, value in mapping or ():
|
||||
tmp.setdefault(key, []).append(value)
|
||||
dict.__init__(self, tmp)
|
||||
|
||||
def __getstate__(self):
|
||||
return dict(self.lists())
|
||||
|
||||
def __setstate__(self, value):
|
||||
dict.clear(self)
|
||||
dict.update(self, value)
|
||||
|
||||
def __iter__(self):
|
||||
return self.iterkeys()
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return the first data value for this key;
|
||||
raises KeyError if not found.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:raise KeyError: if the key does not exist.
|
||||
"""
|
||||
if key in self:
|
||||
return dict.__getitem__(self, key)[0]
|
||||
raise self.KeyError(key)
|
||||
def _clear_lower_keys(self):
|
||||
if hasattr(self, '_lower_keys'):
|
||||
self._lower_keys.clear()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Like :meth:`add` but removes an existing key first.
|
||||
dict.__setitem__(self, key, value)
|
||||
self._clear_lower_keys()
|
||||
|
||||
:param key: the key for the value.
|
||||
:param value: the value to set.
|
||||
"""
|
||||
dict.__setitem__(self, key, [value])
|
||||
def __delitem__(self, key):
|
||||
dict.__delitem__(self, key)
|
||||
self._lower_keys.clear()
|
||||
|
||||
def add(self, key, value):
|
||||
"""Adds a new value for the key.
|
||||
def __contains__(self, key):
|
||||
return key.lower() in self.lower_keys
|
||||
|
||||
.. versionadded:: 0.6
|
||||
def __getitem__(self, key):
|
||||
# We allow fall-through here, so values default to None
|
||||
if key in self:
|
||||
return dict.__getitem__(self, self.lower_keys[key.lower()])
|
||||
|
||||
:param key: the key for the value.
|
||||
:param value: the value to add.
|
||||
"""
|
||||
dict.setdefault(self, key, []).append(value)
|
||||
|
||||
def getlist(self, key, type=None):
|
||||
"""Return the list of items for a given key. If that key is not in the
|
||||
`MultiDict`, the return value will be an empty list. Just as `get`
|
||||
`getlist` accepts a `type` parameter. All items will be converted
|
||||
with the callable defined there.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param type: A callable that is used to cast the value in the
|
||||
:class:`MultiDict`. If a :exc:`ValueError` is raised
|
||||
by this callable the value will be removed from the list.
|
||||
:return: a :class:`list` of all the values for the key.
|
||||
"""
|
||||
try:
|
||||
rv = dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
return []
|
||||
if type is None:
|
||||
return list(rv)
|
||||
result = []
|
||||
for item in rv:
|
||||
try:
|
||||
result.append(type(item))
|
||||
except ValueError:
|
||||
pass
|
||||
return result
|
||||
|
||||
def setlist(self, key, new_list):
|
||||
"""Remove the old values for a key and add new ones. Note that the list
|
||||
you pass the values in will be shallow-copied before it is inserted in
|
||||
the dictionary.
|
||||
|
||||
>>> d = MultiDict()
|
||||
>>> d.setlist('foo', ['1', '2'])
|
||||
>>> d['foo']
|
||||
'1'
|
||||
>>> d.getlist('foo')
|
||||
['1', '2']
|
||||
|
||||
:param key: The key for which the values are set.
|
||||
:param new_list: An iterable with the new values for the key. Old values
|
||||
are removed first.
|
||||
"""
|
||||
dict.__setitem__(self, key, list(new_list))
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
"""Returns the value for the key if it is in the dict, otherwise it
|
||||
returns `default` and sets that value for `key`.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: The default value to be returned if the key is not
|
||||
in the dict. If not further specified it's `None`.
|
||||
"""
|
||||
if key not in self:
|
||||
self[key] = default
|
||||
def get(self, key, default=None):
|
||||
if key in self:
|
||||
return self[key]
|
||||
else:
|
||||
default = self[key]
|
||||
return default
|
||||
return default
|
||||
|
||||
def setlistdefault(self, key, default_list=None):
|
||||
"""Like `setdefault` but sets multiple values. The list returned
|
||||
is not a copy, but the list that is actually used internally. This
|
||||
means that you can put new values into the dict by appending items
|
||||
to the list:
|
||||
class LookupDict(dict):
|
||||
"""Dictionary lookup object."""
|
||||
|
||||
>>> d = MultiDict({"foo": 1})
|
||||
>>> d.setlistdefault("foo").extend([2, 3])
|
||||
>>> d.getlist("foo")
|
||||
[1, 2, 3]
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: An iterable of default values. It is either copied
|
||||
(in case it was a list) or converted into a list
|
||||
before returned.
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
if key not in self:
|
||||
default_list = list(default_list or ())
|
||||
dict.__setitem__(self, key, default_list)
|
||||
else:
|
||||
default_list = dict.__getitem__(self, key)
|
||||
return default_list
|
||||
|
||||
def items(self, multi=False):
|
||||
"""Return a list of ``(key, value)`` pairs.
|
||||
|
||||
:param multi: If set to `True` the list returned will have a
|
||||
pair for each value of each key. Otherwise it
|
||||
will only contain pairs for the first value of
|
||||
each key.
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iteritems(multi))
|
||||
|
||||
def lists(self):
|
||||
"""Return a list of ``(key, values)`` pairs, where values is the list of
|
||||
all values associated with the key.
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iterlists())
|
||||
|
||||
def values(self):
|
||||
"""Returns a list of the first value on every key's value list.
|
||||
|
||||
:return: a :class:`list`.
|
||||
"""
|
||||
return [self[key] for key in self.iterkeys()]
|
||||
|
||||
def listvalues(self):
|
||||
"""Return a list of all values associated with a key. Zipping
|
||||
:meth:`keys` and this is the same as calling :meth:`lists`:
|
||||
|
||||
>>> d = MultiDict({"foo": [1, 2, 3]})
|
||||
>>> zip(d.keys(), d.listvalues()) == d.lists()
|
||||
True
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iterlistvalues())
|
||||
|
||||
def iteritems(self, multi=False):
|
||||
"""Like :meth:`items` but returns an iterator."""
|
||||
for key, values in dict.iteritems(self):
|
||||
if multi:
|
||||
for value in values:
|
||||
yield key, value
|
||||
else:
|
||||
yield key, values[0]
|
||||
|
||||
def iterlists(self):
|
||||
"""Like :meth:`items` but returns an iterator."""
|
||||
for key, values in dict.iteritems(self):
|
||||
yield key, list(values)
|
||||
|
||||
def itervalues(self):
|
||||
"""Like :meth:`values` but returns an iterator."""
|
||||
for values in dict.itervalues(self):
|
||||
yield values[0]
|
||||
|
||||
def iterlistvalues(self):
|
||||
"""Like :meth:`listvalues` but returns an iterator."""
|
||||
return dict.itervalues(self)
|
||||
|
||||
def copy(self):
|
||||
"""Return a shallow copy of this object."""
|
||||
return self.__class__(self)
|
||||
|
||||
def to_dict(self, flat=True):
|
||||
"""Return the contents as regular dict. If `flat` is `True` the
|
||||
returned dict will only have the first item present, if `flat` is
|
||||
`False` all values will be returned as lists.
|
||||
|
||||
:param flat: If set to `False` the dict returned will have lists
|
||||
with all the values in it. Otherwise it will only
|
||||
contain the first value for each key.
|
||||
:return: a :class:`dict`
|
||||
"""
|
||||
if flat:
|
||||
return dict(self.iteritems())
|
||||
return dict(self.lists())
|
||||
|
||||
def update(self, other_dict):
|
||||
"""update() extends rather than replaces existing key lists."""
|
||||
for key, value in iter_multi_items(other_dict):
|
||||
MultiDict.add(self, key, value)
|
||||
|
||||
def pop(self, key, default=_missing):
|
||||
"""Pop the first item for a list on the dict. Afterwards the
|
||||
key is removed from the dict, so additional values are discarded:
|
||||
|
||||
>>> d = MultiDict({"foo": [1, 2, 3]})
|
||||
>>> d.pop("foo")
|
||||
1
|
||||
>>> "foo" in d
|
||||
False
|
||||
|
||||
:param key: the key to pop.
|
||||
:param default: if provided the value to return if the key was
|
||||
not in the dictionary.
|
||||
"""
|
||||
try:
|
||||
return dict.pop(self, key)[0]
|
||||
except KeyError, e:
|
||||
if default is not _missing:
|
||||
return default
|
||||
raise self.KeyError(str(e))
|
||||
|
||||
def popitem(self):
|
||||
"""Pop an item from the dict."""
|
||||
try:
|
||||
item = dict.popitem(self)
|
||||
return (item[0], item[1][0])
|
||||
except KeyError, e:
|
||||
raise self.KeyError(str(e))
|
||||
|
||||
def poplist(self, key):
|
||||
"""Pop the list for a key from the dict. If the key is not in the dict
|
||||
an empty list is returned.
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
If the key does no longer exist a list is returned instead of
|
||||
raising an error.
|
||||
"""
|
||||
return dict.pop(self, key, [])
|
||||
|
||||
def popitemlist(self):
|
||||
"""Pop a ``(key, list)`` tuple from the dict."""
|
||||
try:
|
||||
return dict.popitem(self)
|
||||
except KeyError, e:
|
||||
raise self.KeyError(str(e))
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
super(LookupDict, self).__init__()
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
|
||||
return '<lookup \'%s\'>' % (self.name)
|
||||
|
||||
def __getitem__(self, key):
|
||||
# We allow fall-through here, so values default to None
|
||||
|
||||
return self.__dict__.get(key, None)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.__dict__.get(key, default)
|
||||
72
requests/utils.py
Normal file
72
requests/utils.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.utils
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module provides utlity functions that are used within Requests
|
||||
that are also useful for external consumption.
|
||||
|
||||
"""
|
||||
|
||||
import cookielib
|
||||
|
||||
|
||||
def dict_from_cookiejar(cookiejar):
|
||||
"""Returns a key/value dictionary from a CookieJar."""
|
||||
|
||||
cookie_dict = {}
|
||||
|
||||
for _, cookies in cookiejar._cookies.items():
|
||||
for _, cookies in cookies.items():
|
||||
for cookie in cookies.values():
|
||||
# print cookie
|
||||
cookie_dict[cookie.name] = cookie.value
|
||||
|
||||
return cookie_dict
|
||||
|
||||
|
||||
def cookiejar_from_dict(cookie_dict):
|
||||
"""Returns a CookieJar from a key/value dictionary."""
|
||||
|
||||
# return cookiejar if one was passed in
|
||||
if isinstance(cookie_dict, cookielib.CookieJar):
|
||||
return cookie_dict
|
||||
|
||||
# create cookiejar
|
||||
cj = cookielib.CookieJar()
|
||||
|
||||
cj = add_dict_to_cookiejar(cj, cookie_dict)
|
||||
|
||||
return cj
|
||||
|
||||
|
||||
def add_dict_to_cookiejar(cj, cookie_dict):
|
||||
"""Returns a CookieJar from a key/value dictionary."""
|
||||
|
||||
for k, v in cookie_dict.items():
|
||||
|
||||
cookie = cookielib.Cookie(
|
||||
version=0,
|
||||
name=k,
|
||||
value=v,
|
||||
port=None,
|
||||
port_specified=False,
|
||||
domain='',
|
||||
domain_specified=False,
|
||||
domain_initial_dot=False,
|
||||
path='/',
|
||||
path_specified=True,
|
||||
secure=False,
|
||||
expires=None,
|
||||
discard=True,
|
||||
comment=None,
|
||||
comment_url=None,
|
||||
rest={'HttpOnly': None},
|
||||
rfc2109=False
|
||||
)
|
||||
|
||||
# add cookie to cookiejar
|
||||
cj.set_cookie(cookie)
|
||||
|
||||
return cj
|
||||
9
setup.py
Normal file → Executable file
9
setup.py
Normal file → Executable file
@@ -5,7 +5,10 @@ import os
|
||||
import sys
|
||||
import requests
|
||||
|
||||
from distutils.core import setup
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
|
||||
|
||||
@@ -25,12 +28,12 @@ if sys.version_info[:2] < (2,6):
|
||||
setup(
|
||||
name='requests',
|
||||
version=requests.__version__,
|
||||
description='Awesome Python HTTP Library that\'s actually usable.',
|
||||
description='Python HTTP for Humans.',
|
||||
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',
|
||||
url='http://python-requests.org',
|
||||
packages= [
|
||||
'requests',
|
||||
'requests.packages',
|
||||
|
||||
456
test_requests.py
456
test_requests.py
@@ -1,160 +1,494 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import unittest
|
||||
import cookielib
|
||||
|
||||
try:
|
||||
import omnijson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from requests.sessions import Session
|
||||
|
||||
|
||||
HTTPBIN_URL = 'http://httpbin.ep.io/'
|
||||
HTTPSBIN_URL = 'https://httpbin.ep.io/'
|
||||
|
||||
# HTTPBIN_URL = 'http://staging.httpbin.org/'
|
||||
# HTTPSBIN_URL = 'https://httpbin-staging.ep.io/'
|
||||
|
||||
|
||||
def httpbin(*suffix):
|
||||
"""Returns url for HTTPBIN resource."""
|
||||
|
||||
return HTTPBIN_URL + '/'.join(suffix)
|
||||
|
||||
|
||||
def httpsbin(*suffix):
|
||||
"""Returns url for HTTPSBIN resource."""
|
||||
|
||||
return HTTPSBIN_URL + '/'.join(suffix)
|
||||
|
||||
|
||||
SERVICES = (httpbin, httpsbin)
|
||||
|
||||
|
||||
|
||||
class RequestsTestSuite(unittest.TestCase):
|
||||
"""Requests test cases."""
|
||||
|
||||
# It goes to eleven.
|
||||
_multiprocess_can_split_ = True
|
||||
|
||||
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')
|
||||
r = requests.get(httpbin('/'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_HTTPS_200_OK_GET(self):
|
||||
r = requests.get('https://google.com')
|
||||
r = requests.get(httpsbin('/'))
|
||||
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)
|
||||
r = requests.get(httpbin('user-agent'), headers=heads)
|
||||
|
||||
assert heads['User-agent'] in r.content
|
||||
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)
|
||||
r = requests.get(httpbin('get') + '?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);
|
||||
heads = {
|
||||
'User-agent':
|
||||
'Mozilla/5.0 (github.com/kennethreitz/requests)'
|
||||
}
|
||||
|
||||
r = requests.get(httpbin('user-agent'), headers=heads);
|
||||
self.assertTrue(heads['User-agent'] in r.content)
|
||||
|
||||
heads = {
|
||||
'user-agent':
|
||||
'Mozilla/5.0 (github.com/kennethreitz/requests)'
|
||||
}
|
||||
|
||||
r = requests.get(httpbin('user-agent'), headers=heads);
|
||||
self.assertTrue(heads['user-agent'] in r.content)
|
||||
|
||||
|
||||
def test_HTTP_200_OK_HEAD(self):
|
||||
r = requests.head('http://google.com')
|
||||
r = requests.head(httpbin('/'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_HTTPS_200_OK_HEAD(self):
|
||||
r = requests.head('https://google.com')
|
||||
r = requests.head(httpsbin('/'))
|
||||
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)
|
||||
|
||||
def test_HTTP_200_OK_PUT(self):
|
||||
r = requests.put(httpbin('put'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
r = requests.get(url)
|
||||
|
||||
def test_HTTPS_200_OK_PUT(self):
|
||||
r = requests.put(httpsbin('put'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# reset auto authentication
|
||||
requests.auth_manager.empty()
|
||||
|
||||
def test_HTTP_200_OK_PATCH(self):
|
||||
r = requests.patch(httpbin('patch'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_HTTPS_200_OK_PATCH(self):
|
||||
r = requests.patch(httpsbin('patch'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_AUTH_HTTP_200_OK_GET(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
auth = ('user', 'pass')
|
||||
url = service('basic-auth', 'user', 'pass')
|
||||
|
||||
r = requests.get(url, auth=auth)
|
||||
# print r.__dict__
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
r = requests.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
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)
|
||||
for service in SERVICES:
|
||||
|
||||
post2 = requests.post(bin.url, files={'some': open('test_requests.py')})
|
||||
self.assertEqual(post2.status_code, 201)
|
||||
url = service('post')
|
||||
post = requests.post(url).raise_for_status()
|
||||
|
||||
post = requests.post(url, data={'some': 'data'})
|
||||
self.assertEqual(post.status_code, 200)
|
||||
|
||||
post2 = requests.post(url, files={'some': open('test_requests.py')})
|
||||
self.assertEqual(post2.status_code, 200)
|
||||
|
||||
post3 = requests.post(url, data='[{"some": "json"}]')
|
||||
self.assertEqual(post3.status_code, 200)
|
||||
|
||||
post3 = requests.post(bin.url, data='[{"some": "json"}]')
|
||||
self.assertEqual(post.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)
|
||||
for service in SERVICES:
|
||||
|
||||
post2 = requests.post(bin.url, files={'some': open('test_requests.py')}, data={'some': 'data'})
|
||||
self.assertEqual(post2.status_code, 201)
|
||||
url = service('post')
|
||||
post = requests.post(url,
|
||||
files={'some': open('test_requests.py')},
|
||||
data={'some': 'data'})
|
||||
|
||||
self.assertEqual(post.status_code, 200)
|
||||
|
||||
|
||||
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'})
|
||||
for service in SERVICES:
|
||||
|
||||
url = service('post')
|
||||
|
||||
post2 = requests.post(url,
|
||||
files={'some': open('test_requests.py')},
|
||||
headers = {'User-Agent': 'requests-tests'})
|
||||
|
||||
self.assertEqual(post2.status_code, 200)
|
||||
|
||||
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)
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('status', '500'))
|
||||
self.assertEqual(bool(r), False)
|
||||
|
||||
r = requests.get(service('/'))
|
||||
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)
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('status', '404'))
|
||||
self.assertEqual(r.ok, False)
|
||||
|
||||
|
||||
def test_status_raising(self):
|
||||
r = requests.get('http://google.com/some-404-url')
|
||||
r = requests.get(httpbin('status', '404'))
|
||||
self.assertRaises(requests.HTTPError, r.raise_for_status)
|
||||
|
||||
r = requests.get('http://google.com/')
|
||||
r = requests.get(httpbin('status', '200'))
|
||||
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)
|
||||
url = httpbin('cookies', 'set', 'requests_cookie', 'awesome')
|
||||
r = requests.get(url, cookies=jar)
|
||||
self.assertTrue(jar)
|
||||
|
||||
cookie_found = False
|
||||
for cookie in jar:
|
||||
if cookie.name == 'requests_cookie':
|
||||
self.assertEquals(cookie.value, 'awesome')
|
||||
cookie_found = True
|
||||
self.assertTrue(cookie_found)
|
||||
|
||||
r = requests.get(httpbin('cookies'), cookies=jar)
|
||||
self.assertTrue('awesome' in r.content)
|
||||
|
||||
|
||||
def test_decompress_gzip(self):
|
||||
|
||||
r = requests.get('http://api.stackoverflow.com/1.1/users/495995/top-answer-tags')
|
||||
r = requests.get(httpbin('gzip'))
|
||||
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)
|
||||
|
||||
def test_unicode_get(self):
|
||||
requests.get('http://google.com', params={'foo': u'føø'})
|
||||
requests.get('http://google.com', params={u'føø': u'føø'})
|
||||
requests.get('http://google.com', params={'føø': 'føø'})
|
||||
requests.get('http://google.com', params={'foo': u'foo'})
|
||||
requests.get('http://google.com/ø', params={'foo': u'foo'})
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
url = service('/')
|
||||
|
||||
requests.get(url, params={'foo': u'føø'})
|
||||
requests.get(url, params={u'føø': u'føø'})
|
||||
requests.get(url, params={'føø': 'føø'})
|
||||
requests.get(url, params={'foo': u'foo'})
|
||||
requests.get(service('ø'), params={'foo': u'foo'})
|
||||
|
||||
|
||||
def test_httpauth_recursion(self):
|
||||
conv_auth = ('requeststest', 'bad_password')
|
||||
|
||||
r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
|
||||
self.assertEquals(r.status_code, 401)
|
||||
http_auth = ('user', 'BADpass')
|
||||
|
||||
for service in SERVICES:
|
||||
r = requests.get(service('basic-auth', 'user', 'pass'), auth=http_auth)
|
||||
self.assertEquals(r.status_code, 401)
|
||||
|
||||
|
||||
def test_settings(self):
|
||||
|
||||
def test():
|
||||
r = requests.get(httpbin(''))
|
||||
r.raise_for_status()
|
||||
|
||||
with requests.settings(timeout=0.0000001):
|
||||
self.assertRaises(requests.Timeout, test)
|
||||
|
||||
with requests.settings(timeout=100):
|
||||
requests.get(httpbin(''))
|
||||
|
||||
|
||||
def test_urlencoded_post_data(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'), data=dict(test='fooaowpeuf'))
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
|
||||
self.assertEquals(rbody.get('form'), dict(test='fooaowpeuf'))
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_nonurlencoded_post_data(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'), data='fooaowpeuf')
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
# Body wasn't valid url encoded data, so the server returns None as
|
||||
# "form" and the raw body as "data".
|
||||
self.assertEquals(rbody.get('form'), None)
|
||||
self.assertEquals(rbody.get('data'), 'fooaowpeuf')
|
||||
|
||||
|
||||
def test_urlencoded_post_querystring(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'), params=dict(test='fooaowpeuf'))
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?test=fooaowpeuf'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
self.assertEquals(rbody.get('form'), {}) # No form supplied
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_nonurlencoded_post_querystring(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'), params='fooaowpeuf')
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?fooaowpeuf'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
self.assertEquals(rbody.get('form'), {}) # No form supplied
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_urlencoded_post_query_and_data(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(
|
||||
service('post'),
|
||||
params=dict(test='fooaowpeuf'),
|
||||
data=dict(test2="foobar"))
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?test=fooaowpeuf'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
self.assertEquals(rbody.get('form'), dict(test2='foobar'))
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_nonurlencoded_post_query_and_data(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'),
|
||||
params='fooaowpeuf', data="foobar")
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?fooaowpeuf'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
|
||||
self.assertEquals(rbody.get('form'), None)
|
||||
self.assertEquals(rbody.get('data'), 'foobar')
|
||||
|
||||
|
||||
def test_idna(self):
|
||||
r = requests.get(u'http://➡.ws/httpbin')
|
||||
assert 'httpbin' in r.url
|
||||
|
||||
|
||||
def test_urlencoded_get_query_multivalued_param(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('get'), params=dict(test=['foo','baz']))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.url, service('get?test=foo&test=baz'))
|
||||
|
||||
|
||||
def test_urlencoded_post_querystring_multivalued(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(service('post'), params=dict(test=['foo','baz']))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?test=foo&test=baz'))
|
||||
|
||||
rbody = json.loads(r.content)
|
||||
self.assertEquals(rbody.get('form'), {}) # No form supplied
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_urlencoded_post_query_multivalued_and_data(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.post(
|
||||
service('post'),
|
||||
params=dict(test=['foo','baz']),
|
||||
data=dict(test2="foobar",test3=['foo','baz']))
|
||||
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(r.headers['content-type'], 'application/json')
|
||||
self.assertEquals(r.url, service('post?test=foo&test=baz'))
|
||||
rbody = json.loads(r.content)
|
||||
self.assertEquals(rbody.get('form'), dict(test2='foobar',test3='foo'))
|
||||
self.assertEquals(rbody.get('data'), '')
|
||||
|
||||
|
||||
def test_GET_no_redirect(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('redirect', '3'), allow_redirects=False)
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(len(r.history), 0)
|
||||
|
||||
|
||||
def test_HEAD_no_redirect(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.head(service('redirect', '3'), allow_redirects=False)
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(len(r.history), 0)
|
||||
|
||||
|
||||
def test_redirect_history(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('redirect', '3'))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(len(r.history), 3)
|
||||
|
||||
|
||||
def test_relative_redirect_history(self):
|
||||
|
||||
for service in SERVICES:
|
||||
|
||||
r = requests.get(service('relative-redirect', '3'))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
self.assertEquals(len(r.history), 3)
|
||||
|
||||
|
||||
def test_session_HTTP_200_OK_GET(self):
|
||||
|
||||
s = Session()
|
||||
r = s.get(httpbin('/'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_session_HTTPS_200_OK_GET(self):
|
||||
|
||||
s = Session()
|
||||
r = s.get(httpsbin('/'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
def test_session_persistent_headers(self):
|
||||
|
||||
heads = {'User-agent': 'Mozilla/5.0'}
|
||||
|
||||
s = Session()
|
||||
s.headers = heads
|
||||
# Make 2 requests from Session object, should send header both times
|
||||
r1 = s.get(httpbin('user-agent'))
|
||||
|
||||
assert heads['User-agent'] in r1.content
|
||||
r2 = s.get(httpbin('user-agent'))
|
||||
|
||||
assert heads['User-agent'] in r2.content
|
||||
self.assertEqual(r2.status_code, 200)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user