Compare commits

...

503 Commits

Author SHA1 Message Date
Kenneth Reitz
8b640979ee v0.6.1 2011-08-20 18:25:32 -04:00
Kenneth Reitz
c58ddff3a4 history update 2011-08-20 18:24:58 -04:00
Kenneth Reitz
53a53668d9 no more roadmap 2011-08-19 20:59:16 -04:00
Kenneth Reitz
e7d8982583 Merge branch 'feature/jerem' into develop 2011-08-19 20:12:53 -04:00
Kenneth Reitz
abc5e27931 config max_redirects 2011-08-19 20:12:41 -04:00
Kenneth Reitz
c75eaec852 integrate codes into models.py 2011-08-19 20:12:33 -04:00
Kenneth Reitz
d9a349c1a1 303 status code 2011-08-19 20:12:16 -04:00
Kenneth Reitz
9d143ed653 Merge branch 'develop' of https://github.com/jerem/requests into feature/jerem 2011-08-19 19:58:24 -04:00
Kenneth Reitz
ce4927e5d3 added Den Shabalin to AUTHORS 2011-08-19 19:39:53 -04:00
Den Shabalin
17c551539b Merge branch 'master' of github.com:densh/requests into issue-128 2011-08-19 19:20:54 +03:00
Den Shabalin
2f55393593 Fixes an issue #128: `Response not working with lxml''. This error happend due to lxml's attempt to do .geturl() call on the response object. __getattr__ didn't raise AttributeError so response.geturl'' returned None and response.geturl()'' resulted into `TypeError: 'NoneType' object is not callable'' seen in the issue. 2011-08-19 19:18:26 +03:00
Kenneth Reitz
efbbb84315 Added Mikko Ohtamaa to AUTHORS
#124
2011-08-17 20:17:22 -04:00
Kenneth Reitz
96e7a60b8d Allow any method, if so inclined
#124
2011-08-17 20:15:28 -04:00
Jérémy Bethmont
c22b71fc5b Handle redirection without scheme. 2011-08-17 16:04:53 +02:00
Kenneth Reitz
007963afde authors 2011-08-17 09:45:31 -04:00
Kenneth Reitz
0708736c45 Merge branch 'patch-1' of https://github.com/sharat87/requests into develop 2011-08-17 09:45:23 -04:00
Kenneth Reitz
61d50d531c Authors += Armin 2011-08-17 09:41:24 -04:00
Jérémy Bethmont
a17110b32a Merge remote-tracking branch 'upstream/develop' into develop 2011-08-17 15:32:11 +02:00
Jérémy Bethmont
571d808d25 Fixed bad merge. 2011-08-17 15:31:24 +02:00
Shrikant Sharat Kandula
5e6ecdad9f Typo in documentation
The kwarg is named `headers`, not `header`. Also, its a dict, not a set.
-
Armin Ronacher
91e75f7215 As proposed by @davidbgk this is only consistent with \o/ :) 2011-08-17 13:40:29 +03:00
Kenneth Reitz
81d599786a Merge branch 'develop' 2011-08-17 04:33:34 -04:00
Kenneth Reitz
9471b0ab88 let's do this 2011-08-17 04:32:46 -04:00
Jeremy Selier
470af42bf2 Fixing myself :) 2011-08-17 11:28:47 +03:00
Kenneth Reitz
0b34812afc readme cleanup 2011-08-17 04:23:16 -04:00
Kenneth Reitz
47a36de74e supported interpreters 2011-08-17 04:17:21 -04:00
Kenneth Reitz
0c21aa5530 doc cleanups 2011-08-17 04:16:19 -04:00
Jérémy Bethmont
fe109b27be Merge remote-tracking branch 'upstream/develop' into develop
Conflicts:
	requests/models.py
2011-08-17 10:07:43 +02:00
Kenneth Reitz
3f873dc222 hooks in api layer only 2011-08-17 04:05:20 -04:00
Kenneth Reitz
e0ae2b4155 fixes 2011-08-17 04:03:54 -04:00
Kenneth Reitz
5f603ae787 typo 2011-08-17 04:01:11 -04:00
Kenneth Reitz
d4502aff87 consiserererr 2011-08-17 04:00:12 -04:00
Kenneth Reitz
3992ed6ad1 cookies docs 2011-08-17 03:58:55 -04:00
Kenneth Reitz
b72eb53a73 changing history 2011-08-17 03:25:31 -04:00
Kenneth Reitz
4d0c6cefd0 yawns 2011-08-17 03:21:31 -04:00
Kenneth Reitz
3f8f1dc00c cleaner defaults 2011-08-17 03:19:51 -04:00
Kenneth Reitz
008c77c911 hijack arguments 2011-08-17 03:19:46 -04:00
Kenneth Reitz
77b37687b6 advanced docs 2011-08-17 03:07:24 -04:00
Kenneth Reitz
b7307298d2 merge headers, don't overwrite 2011-08-17 02:49:29 -04:00
Kenneth Reitz
abcf81e2a8 docs update 2011-08-17 02:49:13 -04:00
Kenneth Reitz
e477fce211 add_dict_to_cookiejar
Signed-off-by: Kenneth Reitz <me@kennethreitz.com>
2011-08-17 02:25:56 -04:00
Kenneth Reitz
467766cfe8 cookie session persistence 2011-08-17 02:25:41 -04:00
Kenneth Reitz
66391e5c9d ACTIVVVAATTTEEEEEEE 2011-08-17 02:01:19 -04:00
Kenneth Reitz
f7024a6000 super cookiejar_from_dict powwerrrs 2011-08-17 02:01:13 -04:00
Kenneth Reitz
ec33ce9d29 sp. 2011-08-17 01:32:12 -04:00
Kenneth Reitz
d0aa0175e6 v0.6.0 2011-08-17 01:32:04 -04:00
Kenneth Reitz
d9e5717377 cookiejar_from_dict
#12
2011-08-17 01:29:58 -04:00
Kenneth Reitz
7e255177dc get cookies from response
#116
2011-08-17 01:23:49 -04:00
Kenneth Reitz
b0a0b509da session at root package level 2011-08-17 01:00:00 -04:00
Kenneth Reitz
51038497f6 better 2011-08-17 00:40:49 -04:00
Kenneth Reitz
855c273707 whitespace 2011-08-17 00:40:37 -04:00
Kenneth Reitz
281ab5a8f9 move hooks into api layer only 2011-08-17 00:40:28 -04:00
Kenneth Reitz
7836536459 post_request hook 2011-08-17 00:33:28 -04:00
Kenneth Reitz
2a0eff9c71 docstrings 2011-08-17 00:33:21 -04:00
Kenneth Reitz
3e8a14e133 pre_request hook 2011-08-17 00:28:03 -04:00
Kenneth Reitz
8ef0e88220 attach hooks to Request 2011-08-17 00:27:41 -04:00
Kenneth Reitz
e102c1b4ca RTD doesn't work w/ this 2011-08-17 00:17:58 -04:00
Kenneth Reitz
5ba43d0fe5 hook plans 2011-08-17 00:16:57 -04:00
Kenneth Reitz
783e540d6d sigh 2011-08-17 00:09:47 -04:00
Kenneth Reitz
24da41005c hmmm 2011-08-17 00:09:38 -04:00
Kenneth Reitz
17d3e1d498 whitespace 2011-08-17 00:08:39 -04:00
Kenneth Reitz
8d2ab68a97 space 2011-08-17 00:08:06 -04:00
Kenneth Reitz
7322aa5b64 newlines 2011-08-17 00:02:18 -04:00
Kenneth Reitz
6b11cf21f3 todo 2011-08-17 00:00:47 -04:00
Kenneth Reitz
9322d13f61 cleanups 2011-08-16 23:58:55 -04:00
Kenneth Reitz
86a06c5c18 auth_manager internal 2011-08-16 23:58:07 -04:00
Kenneth Reitz
d0cb56932c new hooks system
#118
2011-08-16 23:57:44 -04:00
Kenneth Reitz
fc04d8a368 cleanups 2011-08-16 22:55:18 -04:00
Kenneth Reitz
519b9cee96 relative imports 2011-08-16 22:55:10 -04:00
Kenneth Reitz
efba606e57 doctoring for Session 2011-08-16 22:20:26 -04:00
Kenneth Reitz
5bb18810d4 memory location repr for requests-client 2011-08-16 22:19:49 -04:00
Kenneth Reitz
853383a8d3 docstring fix 2011-08-16 22:16:22 -04:00
Kenneth Reitz
f319006813 session => sessions 2011-08-16 22:15:03 -04:00
Kenneth Reitz
2fa021582b Merge branch 'feature/session' of https://github.com/tomhsx/requests into feature/session
Conflicts:
	AUTHORS
2011-08-16 02:34:44 -04:00
Tom Hogans
0ed641a26e Added docs regarding requests.session to docs/user/advanced.rst 2011-08-16 02:00:11 -04:00
Tom Hogans
c2fd568695 Added tests for requests.session 2011-08-16 01:38:39 -04:00
Kenneth Reitz
8f72c48548 added alex to authors 2011-08-15 22:17:55 -04:00
alopatin
08a821381a Fixed misc spelling mistakes. 2011-08-15 22:53:22 -03:00
Kenneth Reitz
4625445d2a Merge branch 'develop' of https://github.com/moliware/requests into develop 2011-08-15 21:50:00 -04:00
Tom Hogans
ca428504d4 Added request.session functionality 2011-08-15 16:01:26 -04:00
JensDiemer
88f79b74ce use settings.timeout_fallback bool for socket.setdefaulttimeout() fallback 2011-08-15 17:03:37 +02:00
JensDiemer
647cdd3847 Use socket.setdefaulttimeout(timeout) only if timeout argument doesn't exist (Python <2.6) - https://github.com/kennethreitz/requests/issues/112 2011-08-15 16:51:33 +02:00
moliware
c510d21e4e headers.update is not the same that add headers one by one with add_header 2011-08-15 11:58:57 +02:00
Kenneth Reitz
49e5df1ff9 whoops 2011-08-14 22:32:35 -04:00
Kenneth Reitz
2357b13824 Response Headers quickstart 2011-08-14 22:30:30 -04:00
Kenneth Reitz
f137db6803 raise_for_status quick start 2011-08-14 22:20:03 -04:00
Kenneth Reitz
2cf0dad573 models.. 2011-08-14 22:12:50 -04:00
Kenneth Reitz
414b9dde84 not tablib.. 2011-08-14 22:08:01 -04:00
Kenneth Reitz
d50742f059 massive documentation update 2011-08-14 22:07:15 -04:00
Kenneth Reitz
3bc9a181a1 basic FAQ 2011-08-14 21:16:04 -04:00
Kenneth Reitz
975720a30c I'll have to manually add that to sphinx 2011-08-14 15:50:18 -04:00
Kenneth Reitz
1361506ae6 status blocks 2011-08-14 15:44:03 -04:00
Kenneth Reitz
23ff27ea4b Documentation fixes 2011-08-14 11:43:25 -04:00
Kenneth Reitz
933cbd735b emphasize r.content 2011-08-14 11:06:41 -04:00
Kenneth Reitz
ce1945c780 status codes fix 2011-08-14 10:43:02 -04:00
Kenneth Reitz
f52a413b6a Merge branch 'patch-1' of https://github.com/SimonSapin/requests into develop 2011-08-14 10:41:15 -04:00
Kenneth Reitz
ced96912f3 Merge remote-tracking branch 'origin/develop' into develop 2011-08-14 10:40:32 -04:00
Kenneth Reitz
f6e97c322b Merge branch 'develop' of https://github.com/jbochi/requests into develop 2011-08-14 10:39:47 -04:00
Kenneth Reitz
c9d61aefe4 Merge branch 'docs-typos' of https://github.com/markstory/requests into develop 2011-08-14 10:39:35 -04:00
Kenneth Reitz
85c79658ef Merge pull request #108 from zed/patch-1
fix typo
2011-08-14 07:21:52 -07:00
zed
c4c86fd6e8 fix typo 2011-08-14 17:46:43 +04:00
Mark Story
95dcdd8a05 Fixing a small typo. 2011-08-14 09:13:50 -04:00
Juarez Bochi
43669183ce Edited PATCH Requests in README 2011-08-14 07:30:43 -03:00
Simon Sapin
56e58fdcaf Fix a link in the documentation. 2011-08-14 09:50:25 +03:00
Kenneth Reitz
47378164ef \o/ 2011-08-13 14:55:41 -04:00
Kenneth Reitz
9469b4bb69 status_codes lookup dict 2011-08-13 14:50:59 -04:00
Kenneth Reitz
6cead64345 supporting codes class 2011-08-13 14:50:48 -04:00
Kenneth Reitz
773630b010 requests.codes 2011-08-13 14:50:38 -04:00
Kenneth Reitz
907e529471 changing history muahaha 2011-08-13 13:17:41 -04:00
Kenneth Reitz
2d70647f72 attach request object to responses 2011-08-13 13:17:17 -04:00
Kenneth Reitz
10cc4673f9 remove Response.cached 2011-08-13 13:14:10 -04:00
Kenneth Reitz
629d730da6 all main functions are **kwargs now. People shouldn't be relying on the order. 2011-08-13 13:11:16 -04:00
Kenneth Reitz
d4eabaaacc better 2011-08-13 13:04:14 -04:00
Kenneth Reitz
5693314a37 response error handling rework 2011-08-13 13:01:44 -04:00
Kenneth Reitz
2e4724ccea history 2011-08-13 12:34:13 -04:00
Kenneth Reitz
e7084940e1 added Jeremy Selie to AUTHORS 2011-08-13 09:40:24 -04:00
Kenneth Reitz
b141da36a4 test relative redirects 2011-08-13 09:37:14 -04:00
Kenneth Reitz
bb16fe8115 http/https tests 2011-08-13 09:37:07 -04:00
Kenneth Reitz
a987729712 whitespace! 2011-08-13 09:14:49 -04:00
Kenneth Reitz
f7cc7b1778 Merge branch 'patch-1' of https://github.com/jeremys/requests into develop 2011-08-13 08:39:16 -04:00
Jérémy Bethmont
e1909375f5 Merge branch 'memoryleak' into develop
Conflicts:
	requests/models.py
2011-08-09 17:31:26 +02:00
Jérémy Bethmont
cf94c96a68 Always close connection during redirections. 2011-08-09 17:30:19 +02:00
Jérémy Bethmont
e7e395549f Handle too many redirects. 2011-08-09 17:28:58 +02:00
Jérémy Bethmont
e776242de1 Merge branch 'urlencode' into develop 2011-08-09 15:42:18 +02:00
Jérémy Bethmont
7b79cea738 Use urljoin instead of manual concatenation. 2011-08-09 15:41:59 +02:00
Jérémy Bethmont
32a861b2ed Fixed memory leak (see http://bugs.python.org/issue1208304) 2011-08-09 15:34:44 +02:00
Jérémy Bethmont
2ce26d4a1c Merge remote-tracking branch 'upstream/develop' into develop 2011-08-09 15:16:23 +02:00
Jérémy Bethmont
aa0f78b740 Better support of utf-8 paths. 2011-08-09 15:14:08 +02:00
Zbigniew Siciarz
e4adef5226 Added proxies to requests.settings 2011-08-09 10:03:09 +02:00
Jérémy Bethmont
7dc581fad9 Merge branch 'develop' into urlencode 2011-08-08 18:14:08 +02:00
Jérémy Bethmont
0b6dc091f5 Added support for URLs with path not encoded. 2011-08-08 18:11:33 +02:00
Kenneth Reitz
e1714e678e Added Peter Manser to AUTHORS 2011-08-04 22:08:32 -03:00
Kenneth Reitz
d10b08ff88 Merge pull request #99 from petermanser/develop
Fixing minor bug in code example - missing apostrophe
2011-08-04 18:07:49 -07:00
Peter Manser
18c42e88f5 Fixing minor bug in code example - missing apostrophe 2011-08-04 21:47:15 +02:00
Kenneth Reitz
f238dffc18 Merge https://github.com/bclermont/requests into develop 2011-08-02 23:23:41 -04:00
Jeremy Selier
5a7ec361f4 Join with the latest URL not the first one. 2011-08-02 18:51:21 +03:00
Jeremy Selier
291d4742d6 Fixes correct "relative" redirections. Example: http://digg.com/d1qIwX, which redirects to "/news/story/A_Manifesto_How_to_Save_Media", which requests transforms to: http://digg.com//news/story/A_Manifesto_How_to_Save_Media" which results in an endless redirect loop. 2011-08-02 18:05:03 +03:00
Kenneth Reitz
95ba6fcab2 Merge branch 'release/0.5.1' 2011-07-23 22:59:15 -04:00
Kenneth Reitz
1cdd1d04ce v0.5.1 2011-07-23 22:58:32 -04:00
Kenneth Reitz
3293c0e8f4 Python 2.5 bugfix 2011-07-23 22:49:01 -04:00
Kenneth Reitz
074150ff2c test fix 2011-07-23 22:42:51 -04:00
Kenneth Reitz
c3c90bb09c default headers and content encoding settings
Closes #78
2011-07-23 22:35:46 -04:00
Kenneth Reitz
87560d65ca redirect history length test 2011-07-23 22:16:21 -04:00
Kenneth Reitz
7e4f7fa600 added Rubén Abad to AUTHORS 2011-07-23 22:12:41 -04:00
Kenneth Reitz
7f9c6233bb history bugfix in history 2011-07-23 22:12:34 -04:00
Kenneth Reitz
9b6a190652 Merge branch 'develop' of https://github.com/rabad/requests into develop 2011-07-23 22:11:59 -04:00
Kenneth Reitz
1e1fb1e7a8 Redirect history fix (Closes #91) 2011-07-23 22:11:54 -04:00
Kenneth Reitz
080db5ee5d CaseInsensitiveDict notes 2011-07-23 21:49:08 -04:00
Kenneth Reitz
2edda29448 fixing bad url shortner link 2011-07-23 21:48:28 -04:00
Kenneth Reitz
6ba3523a26 Merge branch 'develop' of https://github.com/tgulacsi/requests into develop 2011-07-23 21:45:45 -04:00
Kenneth Reitz
87e7a04209 v0.5.1 history update 2011-07-23 21:42:12 -04:00
Kenneth Reitz
260e32a7c3 default user agent! 2011-07-23 21:39:14 -04:00
Kenneth Reitz
5fff2c757b forced_basic default handler. 2011-07-23 21:37:17 -04:00
Kenneth Reitz
30d699b64f Forced Basic Auth for 404'd resources 2011-07-23 21:36:24 -04:00
Kenneth Reitz
7d91d3fb5c use patched HTTPBasicAuthHandler 2011-07-23 20:10:37 -04:00
Gulácsi Tamás
747f882b0e structures: CaseInsensitiveDict.__delitem__ missing "value" fix 2011-07-23 22:25:07 +02:00
Gulácsi Tamás
369e7cb38f add myself to AUTHORS 2011-07-23 22:19:28 +02:00
Gulácsi Tamás
04c456b877 structures: little bit more general, lower-key-caching version of CaseInsensitiveDict 2011-07-23 22:16:12 +02:00
Gulácsi Tamás
f408829d51 test_request: simplejson is just as good as omnijson for test 2011-07-23 22:15:28 +02:00
Rubén Abad
73b414ab45 We can represent multivalued params with arrays 2011-07-13 11:08:54 +02:00
Kenneth Reitz
f18c40fd36 👍 2011-07-09 10:41:44 -04:00
Jérémy Bethmont
891e52d723 Added the possibility to access headers without fetching the whole body (fixes #86). 2011-06-29 11:10:39 +02:00
Xu Pan
d2ff0f40eb add doseq to urlencode in model._encode_params to enable list as dict value 2011-06-26 13:32:19 +08:00
Kenneth Reitz
787110c853 international domain name support! 2011-06-24 20:32:28 -04:00
Kenneth Reitz
20319044a3 Added Jérémy Bethmont to AUTHORS. 2011-06-24 18:47:41 -04:00
Kenneth Reitz
6f08c81698 note about indns 2011-06-24 18:24:44 -04:00
Jérémy Bethmont
868cd3d1cb Implemented idna (fixes #76). 2011-06-24 12:21:25 +02:00
Kenneth Reitz
39edf7a7fa raise_for_status +URLError 2011-06-23 06:22:46 -04:00
Kenneth Reitz
4cf4362610 typos 2011-06-23 06:06:56 -04:00
Kenneth Reitz
0b719f037c ! 2011-06-23 05:34:44 -04:00
Kenneth Reitz
9603589b06 oops 2011-06-23 05:32:05 -04:00
Kenneth Reitz
5f38c1209e TURTLES 2011-06-23 05:18:13 -04:00
Kenneth Reitz
749edb3554 new sidebar logo 2011-06-23 05:16:13 -04:00
Kenneth Reitz
882b465968 tox fix 2011-06-23 05:15:09 -04:00
Kenneth Reitz
784fe8bbdb working requests logo 2011-06-23 03:30:32 -04:00
Kenneth Reitz
6253cf4c18 TODO Docs 2011-06-23 03:30:22 -04:00
Kenneth Reitz
8521075868 playing nice. 2011-06-21 22:55:35 -04:00
Kenneth Reitz
2d98ca7477 Merge branch 'release/0.5.0' 2011-06-21 22:44:02 -04:00
Kenneth Reitz
e2d6a92150 Timeouts are normal errors now.
--
FIX ALL TEH TESTS!
2011-06-21 22:43:15 -04:00
Kenneth Reitz
162b751f6d Timeouts are normal errors now.
--
BREAK ALL TEH TESTS!
2011-06-21 22:40:27 -04:00
Kenneth Reitz
20aed5488b Release the v0.5.0! 2011-06-21 22:26:06 -04:00
Kenneth Reitz
2374d0fd7a so many paramatersssss 2011-06-21 22:22:46 -04:00
Kenneth Reitz
483d00736c readme update 2011-06-21 22:22:02 -04:00
Kenneth Reitz
15de7d9dba add PATCH to docs 2011-06-21 22:21:54 -04:00
Kenneth Reitz
34a583b821 PATCH in Docs 2011-06-21 22:06:31 -04:00
Kenneth Reitz
2da86daa3a update history 2011-06-21 22:03:56 -04:00
Kenneth Reitz
b4eac4c993 redirect fix 2011-06-21 22:00:04 -04:00
Kenneth Reitz
b4a4f38cce verbose output stream support 2011-06-21 21:59:57 -04:00
Kenneth Reitz
8c81cf64de proper PATCH testing 2011-06-21 18:24:28 -04:00
Kenneth Reitz
00f066a467 PATCH support 2011-06-21 18:03:06 -04:00
Kenneth Reitz
bdf9f90c8a omnijson for tests 2011-06-21 17:59:24 -04:00
Kenneth Reitz
428678b2e4 Added Alberto Paro to AUTHORS 2011-06-21 17:35:29 -04:00
Alberto Paro
bcd30f05ab Added setuptools management to setup.py to allow to install in develop mode 2011-06-21 22:48:31 +02:00
Kenneth Reitz
bd49da067f updated history 2011-06-21 00:07:41 -04:00
moliware
3e7c682e66 Support for proxies 2011-06-19 22:36:50 +02:00
Kenneth Reitz
bbd1e40bd5 no pypy default 2011-06-17 14:09:33 -04:00
Kenneth Reitz
a25f5ff1dd that works 2011-06-17 13:51:29 -04:00
Richard Boulton
80d860d2d4 Allow POST and PUT requests to take both querystring params and request body
data.
2011-06-16 15:48:43 +01:00
Richard Boulton
f31ade335d Change documentation comments for "params" and "data" parameters to refer to
the request body or the query string, as appropriate.  "GET parameters", "POST
data", etc aren't the ideal terminology to use - they come from common usage
with web browsers, rather than the HTTP specification, so are off-putting in
other contexts.
2011-06-16 15:40:14 +01:00
Richard Boulton
023ab755e5 Move the code to encode the request data into a static helper method - I think this makes the code clearer, but it also prepares the way for reusing the encoding code 2011-06-16 13:35:33 +01:00
Richard Boulton
4320f81bf4 Actually use the HTTPSBIN variable for test https requests 2011-06-16 13:06:22 +01:00
Kenneth Reitz
47f1f28385 + Richard Boulton to AUTHOURS 2011-06-16 06:27:13 -04:00
Richard Boulton
3d1444b829 Update documentation of the "data" parameter for requests.post() to indicate that it allows bytes. Update documentation of requests.put() to refer to the "data" parameter rather than the (hidden, but functional) "params" parameter, and note that that also allows both bytes and dictionaries. 2011-06-16 03:18:47 -07:00
Zbigniew Siciarz
4d3a9defd2 Porting cookie test to httpbin.org. 2011-06-16 09:08:32 +02:00
Zbigniew Siciarz
1f1211747b Merge remote-tracking branch 'upstream/develop' into develop 2011-06-16 08:56:38 +02:00
Kenneth Reitz
45e30048f0 port request tests to httpbin.org 2011-06-16 00:11:47 -04:00
Kenneth Reitz
245c6edec3 todo 2011-06-15 22:56:14 -04:00
Kenneth Reitz
cb0a96fbf3 Redirect chain flag default off. 2011-06-15 22:43:26 -04:00
Zbigniew Siciarz
fade9e36b6 Merge branch 'develop' of git://github.com/kennethreitz/requests into develop 2011-06-13 12:41:40 +02:00
Zbigniew Siciarz
24e2a05605 Merge branch 'master' of git://github.com/kennethreitz/requests into develop 2011-06-13 12:41:33 +02:00
Kenneth Reitz
1749260c97 update tests 2011-06-12 22:02:35 -04:00
Kenneth Reitz
3b86c3c0e3 httpbin 2011-06-12 22:00:17 -04:00
Kenneth Reitz
8f3da7b72c added Daniele Tricoli 'Eriol' to authors 2011-06-12 13:14:56 -04:00
Kenneth Reitz
d6cde6a5df add Zbigniew Siciarz to authors 2011-06-09 18:09:39 -04:00
Zbigniew Siciarz
54fd24b757 Testing that cookies actually work. 2011-06-09 12:30:43 +02:00
Kenneth Reitz
2153b21152 less hostility 2011-06-08 19:22:19 -04:00
Kenneth Reitz
74013d751c rackspace cloud's net connection is insane 2011-06-08 19:18:11 -04:00
Kenneth Reitz
1fa1cdd2a3 WHITESPACE 2011-06-08 19:11:08 -04:00
Kenneth Reitz
cd0d0a2b17 whitespace 2011-06-05 21:22:35 -04:00
Kenneth Reitz
e63569041a Update AUTHORS
Refs #46
2011-05-27 08:02:57 -04:00
Kenneth Reitz
54449483b6 Merge branch 'patch-1' of https://github.com/JNRowe/requests into develop 2011-05-27 08:00:22 -04:00
Kenneth Reitz
52bedf4c0b default post data to empty byte string
Refs #45
2011-05-27 07:59:28 -04:00
James Rowe
5962c0ea86 Subclass dict for CaseInsensitiveDict. 2011-05-27 00:17:44 -07:00
Johannes
9b2d7382bf Conform api.* arguments to api.request arguments.
Fixes #40.
Fixes #41.
2011-05-27 08:36:03 +02:00
Bruno Clermont
d91af4cadf add debian packaging 2011-05-26 15:37:31 -04:00
Kenneth Reitz
7e3d265b0f gauge.es 2011-05-24 18:38:45 -04:00
Kenneth Reitz
f990a7b453 Merge branch 'develop' of github.com:kennethreitz/requests into develop 2011-05-24 17:18:28 -04:00
Kenneth Reitz
ef02d02b1f testimonial cleanup 2011-05-24 17:18:19 -04:00
Kenneth Reitz
04c169ecfe Merge pull request #39 from mrtazz/patch-1
e.g. should be i.e. I guess
2011-05-23 08:42:52 -07:00
Daniel Schauenberg
5593f12347 e.g. should be i.e. I guess 2011-05-23 08:39:15 -07:00
Kenneth Reitz
3189d8690e refactor^2 2011-05-22 18:01:21 -04:00
Kenneth Reitz
b51e042e73 Merge pull request #38 from jgorset/requests
---

Ive refactored `api.request`. I think. The new version is more robust in that it no longer accepts arbitrary arguments and raises an exception if one attempts to call it with both `params` and `data` (that should probably be supported, though, but thats a topic for another issue).

Conflicts:
	requests/api.py
2011-05-22 17:54:10 -04:00
Johannes
82f1320ed5 Refactor api.request 2011-05-22 23:49:29 +02:00
Kenneth Reitz
add6feab02 Merge branch 'release/0.4.1' 2011-05-22 14:16:48 -04:00
Kenneth Reitz
670c4aecdd version bump (v0.4.1) 2011-05-22 14:13:49 -04:00
Kenneth Reitz
215d3984cc setup cleanup 2011-05-22 13:58:37 -04:00
Kenneth Reitz
0b36d925f6 remove debug print 2011-05-22 13:56:57 -04:00
Kenneth Reitz
bc290f62d7 defaults 2011-05-22 13:55:13 -04:00
Kenneth Reitz
c0faa9e79c new settings location 2011-05-22 13:31:17 -04:00
Kenneth Reitz
f887906ec3 cleanup — comments 2011-05-22 13:31:09 -04:00
Kenneth Reitz
982fd9d803 new config module 2011-05-22 13:30:55 -04:00
Kenneth Reitz
d64bf501d0 no import packages 2011-05-22 13:30:22 -04:00
Kenneth Reitz
49f1ec1efb settings -> config
reimpliment settings as singleton
2011-05-22 13:27:17 -04:00
Kenneth Reitz
0741448798 pypy binary moved 2011-05-22 12:14:50 -04:00
Kenneth Reitz
8a4d5c4683 useragent 2011-05-22 12:10:56 -04:00
Kenneth Reitz
3cdb777c23 restore allow_redirect behavior 2011-05-22 11:35:01 -04:00
Kenneth Reitz
ddc5452978 Merge pull request #37 from jgorset/requests
---

![Meme](http://i.imgur.com/kXzcW.jpg)
2011-05-22 11:25:31 -04:00
Johannes
8aa6fabd4d Support relative redirects. Fixes #36 2011-05-22 11:41:15 +02:00
Kenneth Reitz
398f5b501f not tablib.. 2011-05-21 16:24:28 -04:00
Kenneth Reitz
8d9bcefa9d Merge pull request #35 from jgorset/requests
---

Ive completed the migration of the requests settings to the new `settings` module, but my attempt to facilitate for manipulating settings outside of the context manager (eg. `requests.settings.timeout = 5`) has been thwarted by the namespace collision of the context manager and the module.

Im probably just being stupid. Youll figure it out.

Conflicts:
	requests/api.py
2011-05-21 13:04:32 -04:00
Kenneth Reitz
0b29964bf8 updated history 2011-05-21 13:00:21 -04:00
Kenneth Reitz
06e662be86 python2.5 with 2011-05-21 12:47:29 -04:00
Kenneth Reitz
e153b94190 Improved redirection behavior for 301/303 2011-05-21 12:44:58 -04:00
Johannes
7d0374df2f Slight refactor 2011-05-20 22:50:50 +02:00
Johannes
2f60748c73 Stop importing inspect 2011-05-20 20:45:51 +02:00
Johannes
bbd58b922b Migrate settings to new module 2011-05-20 20:32:39 +02:00
Kenneth Reitz
21e51b504e no toys 2011-05-20 12:55:36 -04:00
Kenneth Reitz
ba79a2eb97 new settings module 2011-05-20 12:54:42 -04:00
Johannes
ef81c16611 Fix typos 2011-05-20 18:34:43 +02:00
Johannes
da26f1f377 Adjust tests 2011-05-20 18:31:23 +02:00
Johannes
c8cc2d0421 Relocate exceptions 2011-05-20 18:20:17 +02:00
Johannes
a00dc1abf3 Raise Timeout on request timeouts 2011-05-20 18:17:46 +02:00
Johannes
59c747b793 Fix typos 2011-05-19 23:32:27 +02:00
Johannes Gorset
baddc5b6ad Refactor Request#__init__ 2011-05-19 10:16:54 +02:00
Johannes Gorset
637d0330c2 Add test for non-urlencoded post data 2011-05-19 10:14:15 +02:00
Johannes Gorset
21e2d0c34a Add tests for settings 2011-05-19 10:13:42 +02:00
Johannes Gorset
ec5f227381 Refactor settings 2011-05-19 10:13:37 +02:00
Kenneth Reitz
8ffb2462f9 home page url change 2011-05-18 10:11:07 -04:00
Kenneth Reitz
12187155b4 urllib2 2011-05-16 01:38:48 -04:00
Kenneth Reitz
d7f49f3aa5 no quickstart till done 2011-05-16 01:33:33 -04:00
Kenneth Reitz
461497f5be "a kennethreitz project" 2011-05-16 01:31:54 -04:00
Kenneth Reitz
7929bb91be api update 2011-05-16 01:18:09 -04:00
Kenneth Reitz
6e13e7343d Big API update 2011-05-16 01:13:05 -04:00
Kenneth Reitz
f9f3b46ef6 not tablib 2011-05-16 00:10:30 -04:00
Kenneth Reitz
cc851818c2 doc fixes (pretty) 2011-05-15 23:24:25 -04:00
Kenneth Reitz
ba45b9d890 Major docs cleanup 2011-05-15 23:19:19 -04:00
Kenneth Reitz
9fd70c209b GA and CR 2011-05-15 23:00:15 -04:00
Kenneth Reitz
b7970c2a46 config update 2011-05-15 22:58:17 -04:00
Kenneth Reitz
a7d072a7c0 features philosophy 2011-05-15 21:29:49 -04:00
Kenneth Reitz
054891ab5e can/cant 2011-05-15 21:22:40 -04:00
Kenneth Reitz
d18ea48b95 docs update 2011-05-15 21:14:51 -04:00
Kenneth Reitz
d39cde206b installation 2011-05-15 21:01:48 -04:00
Kenneth Reitz
58d5e0318e api.py -> api.rst 2011-05-15 21:01:44 -04:00
Kenneth Reitz
3e23e48239 docs intro 2011-05-15 20:46:36 -04:00
Kenneth Reitz
f1f5788066 docs refactor 2011-05-15 20:46:28 -04:00
Kenneth Reitz
928facfcd9 better links 2011-05-15 20:34:40 -04:00
Kenneth Reitz
70857f3097 example udpate 2011-05-15 20:25:01 -04:00
Kenneth Reitz
da726fc030 webkit scrollbars 2011-05-15 20:24:55 -04:00
Kenneth Reitz
874a874ea6 better index 2011-05-15 17:52:09 -04:00
Kenneth Reitz
0f76e17258 index cleanup 2011-05-15 17:49:59 -04:00
Kenneth Reitz
6b36911260 testimonials 2011-05-15 17:28:16 -04:00
Kenneth Reitz
1ebf3afb24 orgs 2011-05-15 13:23:05 -04:00
Kenneth Reitz
84544b1d01 link fix 2011-05-15 13:14:22 -04:00
Kenneth Reitz
319b570f82 much better index 2011-05-15 13:13:41 -04:00
Kenneth Reitz
f7481adf45 better verbage 2011-05-15 13:05:22 -04:00
Kenneth Reitz
9b165629f9 better title 2011-05-15 13:01:58 -04:00
Kenneth Reitz
79b43b2b6e better 2011-05-15 12:49:19 -04:00
Kenneth Reitz
80b71a03ce initial doc index 2011-05-15 12:43:34 -04:00
Kenneth Reitz
2546bafdde name fix 2011-05-15 11:35:06 -04:00
Kenneth Reitz
d27e4bff1e ignore builds 2011-05-15 00:41:26 -04:00
Kenneth Reitz
27dd96cf3d config update 2011-05-15 00:41:00 -04:00
Kenneth Reitz
3e45e4ba51 added krthemes 2011-05-15 00:38:59 -04:00
Kenneth Reitz
0ee249247a updated history for v0.4.0 2011-05-15 00:00:44 -04:00
Kenneth Reitz
0f520a8c69 Merge branch 'release/0.4.0' into develop 2011-05-14 23:58:07 -04:00
Kenneth Reitz
aa33194606 Merge branch 'release/0.4.0' 2011-05-14 23:58:00 -04:00
Kenneth Reitz
51360e1ede version bump 2011-05-14 23:57:53 -04:00
Kenneth Reitz
af0febe8ad history update 2011-05-14 23:57:19 -04:00
Kenneth Reitz
155863bf71 readme update 2011-05-14 23:55:15 -04:00
Kenneth Reitz
3cec61481c ignore http errors in redirect loops 2011-05-14 23:22:26 -04:00
Kenneth Reitz
5708fe9565 python 2.5 fix 2011-05-14 23:21:58 -04:00
Kenneth Reitz
c40deefb8a cleanup mess 2011-05-14 23:03:31 -04:00
Kenneth Reitz
cd8ce63a3b Custom RedirectHandler and .history support.
Closes #23
2011-05-14 23:01:13 -04:00
Kenneth Reitz
35beda21b8 pass things into the dict 2011-05-14 22:02:14 -04:00
Kenneth Reitz
85b8b2bd44 CaseInsensitiveDict for Headers 2011-05-14 22:02:07 -04:00
Kenneth Reitz
29aa7736ca Headers: Default to None 2011-05-14 21:52:33 -04:00
Kenneth Reitz
e8ff405fce remote async __all__ 2011-05-14 21:50:41 -04:00
Kenneth Reitz
5172d24e66 New CaseInsensitiveDict 2011-05-14 21:50:25 -04:00
Kenneth Reitz
dfe7d877eb date header on history 2011-05-14 14:46:47 -04:00
Kenneth Reitz
0c00a17372 Merge branch 'release/0.3.4' 2011-05-14 14:31:24 -04:00
Kenneth Reitz
fa1db95814 Version bump: v0.3.4 2011-05-14 14:30:36 -04:00
Kenneth Reitz
d77c0ec138 history update 2011-05-14 14:29:41 -04:00
Kenneth Reitz
8ba12ff4f3 cleaner patch names 2011-05-14 14:24:12 -04:00
Kenneth Reitz
14ef462263 package refactor 2011-05-14 14:21:42 -04:00
Kenneth Reitz
1421ffa32a HTTP Basic recursion. Fixes #31 2011-05-14 14:02:36 -04:00
Kenneth Reitz
62931b60f8 history for v0.3.4 2011-05-14 13:11:08 -04:00
Kenneth Reitz
b1e28c7fd8 Added Megan Emurayama to authors 2011-05-14 13:04:49 -04:00
Kenneth Reitz
9b0bb871d2 Merge pull request #29 from meganemura/requests
---

raised Attribute Error when post str data something like json because of not having items attribute.
move encoding data to utf-8 inside hasattr(data, items)
2011-05-12 13:36:45 -04:00
megane murayama
1971bdbe67 fix for post non-dict data 2011-05-12 12:50:09 -04:00
Kenneth Reitz
912f48eece slight refactor 2011-05-12 10:12:04 -04:00
Kenneth Reitz
86fb0cb235 Merge https://github.com/jgorset/requests into develop 2011-05-12 10:09:00 -04:00
Johannes Gorset
23ee58d6ec Encode both keys and values for incoming unicode data as UTF-8, coincidentally fixing a bug that caused UTF-8 encoded byte strings to be encoded twice and causing an UnicodeDecodeError. 2011-05-12 10:27:41 +02:00
Kenneth Reitz
9e0dcae0aa Merge branch 'release/0.3.3' into develop 2011-05-12 04:02:18 -04:00
Kenneth Reitz
7215452da5 Merge branch 'release/0.3.3' 2011-05-12 04:02:16 -04:00
Kenneth Reitz
01b22a7e12 version bump 2011-05-12 04:02:06 -04:00
Kenneth Reitz
d28804cc48 update history 2011-05-12 03:57:17 -04:00
Kenneth Reitz
a046a04aa2 Merge branch 'feature/timeout' into develop 2011-05-12 03:56:13 -04:00
Kenneth Reitz
59dd3ed459 unicode urls 2011-05-12 03:20:27 -04:00
Kenneth Reitz
b6f6048cff Encode incoming data.
Closes #27.
2011-05-12 03:14:09 -04:00
Kenneth Reitz
8a4a05aac0 test for unicode url issues 2011-05-12 03:12:30 -04:00
Kenneth Reitz
64d6c3074f sphinx extension fix 2011-05-12 00:29:31 -04:00
Kenneth Reitz
45ca6d8a78 Merge pull request #26 from jgorset/requests
---

Ive implemented a `settings` context manager as described in [pull request #25](https://github.com/kennethreitz/requests/pull/25#issuecomment-1039541).

    >>> import requests
    >>> with requests.settings(timeout=0.5):
    ...     requests.get(http://example.org) # Times out after 0.5 seconds
    ...     requests.get(http://example.org, timeout=10) # Times out after 10 seconds

Settings may also be manipulated separately:

    >>> import requests
    >>> requests.timeout = 0.5
    >>> requests.get(http://example.org) # Times out after 0.5 seconds
2011-04-21 17:24:43 -04:00
Johannes
77f4b8a75e Implement settings context manager 2011-04-21 18:38:33 +02:00
Kenneth Reitz
122c4f0514 updated history w/ latest changes 2011-04-21 10:37:22 -04:00
Kenneth Reitz
83494152fe Merge branch 'timeout' of https://github.com/jgorset/requests into develop 2011-04-21 10:28:26 -04:00
Kenneth Reitz
f6f636f74c Merge pull request #24 from jgorset/requests
---

I made some insignificant corrections to the README.
2011-04-21 10:16:54 -04:00
Johannes
39e9424f01 Update AUTHORS 2011-04-21 14:55:50 +02:00
Johannes
d93731213a Implement optional timeout for request functions 2011-04-21 14:54:30 +02:00
Johannes
f31dc8a9ef Substitute 'request' for 'requests' 2011-04-21 14:17:02 +02:00
Johannes
4687951a04 Substitute <request object> for the actual return value of the request functions 2011-04-21 14:01:43 +02:00
Kenneth Reitz
576cba81d9 Merge branch 'master' into develop 2011-04-15 18:00:37 -04:00
Kenneth Reitz
d3a46c822f preliminary multidicts 2011-04-15 18:00:10 -04:00
Kenneth Reitz
ea86602765 Merge branch 'release/0.3.2' into develop 2011-04-15 17:29:38 -04:00
Kenneth Reitz
fec0d1a9a0 history changes 2011-04-15 17:29:26 -04:00
Kenneth Reitz
642c667566 Merge branch 'release/0.3.2' 2011-04-15 17:27:17 -04:00
Kenneth Reitz
b8b87d416e install simplejson if python < 2.6 2011-04-15 17:27:02 -04:00
Kenneth Reitz
6e14d4704d version bump 2011-04-15 17:26:51 -04:00
Kenneth Reitz
f494cd9c72 add autoauth tuple http basic test 2011-04-15 17:24:03 -04:00
Kenneth Reitz
2814664e91 Fixes #20. 2011-04-15 17:21:38 -04:00
Kenneth Reitz
1359094cc8 test for gzip decompress 2011-04-14 19:46:35 -04:00
Kenneth Reitz
7a62b10ff2 Automatically decompress gzipped responses if content-type is set. fixes #19 2011-04-14 19:44:29 -04:00
Kenneth Reitz
23d5761bd4 accept any extra args to r.read() 2011-04-02 18:22:05 -04:00
Kenneth Reitz
cc16073c36 Merge branch 'develop' 2011-04-01 14:55:24 -04:00
Kenneth Reitz
ed8ff63048 Version bump to 0.3.1 2011-04-01 14:53:53 -04:00
Kenneth Reitz
267a852ba6 Update history 2011-04-01 14:52:33 -04:00
Kenneth Reitz
f2b04f94ca Response.read() [file like object] 2011-03-31 04:47:02 -04:00
Kenneth Reitz
e350bea167 added Request.read 2011-03-31 04:46:19 -04:00
robmadole
2401f14975 Fixing User-agent header problem introduced with Python 2.7.1 2011-03-30 00:07:24 -05:00
Kenneth Reitz
be228043a1 whitespace fix 2011-03-21 20:45:37 -04:00
Kenneth Reitz
48ffb627fe added Aram Dulyan to authors 2011-03-09 13:22:47 -05:00
Aram Dulyan
89192c64f0 An empty cookiejar evaluates to False, so it needs to be compared to None for the cookie functionality to work. 2011-03-09 22:31:13 +11:00
Kenneth Reitz
a67cc5c5a9 someone stole the cookie jar 2011-03-06 19:19:22 -05:00
Kenneth Reitz
fa2f1c5f60 Merge branch 'develop' of https://github.com/robmadole/requests into develop 2011-03-06 19:08:32 -05:00
Kenneth Reitz
1d021c5cc1 Added Rob Madole to AUTHORS 2011-03-06 19:07:29 -05:00
Kenneth Reitz
e17111a5dd testing requests with poster and adding headers 2011-03-06 18:50:28 -05:00
Kenneth Reitz
9fdee250de Better POSTER header compatibility (Fixes #13) 2011-03-06 18:48:44 -05:00
Kenneth Reitz
c6acfef3d0 Added Justin Murphy to AUTHORS 2011-03-06 18:48:14 -05:00
robmadole
e4c690e7bc Initial support for cookies 2011-03-01 09:21:00 -06:00
robmadole
ee43ad2497 Ignore those compiled files 2011-03-01 09:15:14 -06:00
Kenneth Reitz
044252ea7b Merge branch 'release/0.3.0' 2011-02-25 09:00:23 -05:00
Kenneth Reitz
f8032e08ae tests cleanups 2011-02-25 08:58:24 -05:00
Kenneth Reitz
f0d2ac3dd5 Readme update 2011-02-25 08:58:17 -05:00
Kenneth Reitz
270898e521 Readme Updates 2011-02-25 08:48:09 -05:00
Kenneth Reitz
897dd3aaa8 whitespace 2011-02-25 08:11:17 -05:00
Kenneth Reitz
dd01f8661e hacking whitespace fixes 2011-02-25 08:11:10 -05:00
Kenneth Reitz
99c3970fd3 history 2011-02-25 08:04:03 -05:00
Kenneth Reitz
839c8cf150 postbin tests were broken 2011-02-25 08:00:23 -05:00
Kenneth Reitz
b252cd8403 allow files to be upload along with form data 2011-02-25 08:00:11 -05:00
Kenneth Reitz
b3b2b19212 hmmm 2011-02-25 07:39:19 -05:00
Kenneth Reitz
a74e954788 test improvements 2011-02-25 07:21:11 -05:00
Kenneth Reitz
32f75585ad url builder, cleanups 2011-02-25 07:20:59 -05:00
Kenneth Reitz
9a8b25d5fa makin' history 2011-02-25 07:20:37 -05:00
Kenneth Reitz
14ba99f975 prettier imports 2011-02-25 05:03:12 -05:00
Kenneth Reitz
ccb7fdd8c2 Version bump 2011-02-25 04:48:10 -05:00
Kenneth Reitz
9a1d90d184 Proper async module docs 2011-02-25 04:43:18 -05:00
Kenneth Reitz
22798ca32b Prepping for v0.3.0 release. 2011-02-25 04:40:33 -05:00
Kenneth Reitz
3fe4753d5d Cleanups 2011-02-24 02:06:48 -05:00
Kenneth Reitz
d91157c567 Merge branch 'develop' into merge/xero 2011-02-24 01:53:55 -05:00
Kenneth Reitz
e7b8e9371a merge changes 2011-02-24 01:51:53 -05:00
Kenneth Reitz
1f60e1004c Merge https://github.com/Digitalxero/requests into merge/xero 2011-02-24 01:45:44 -05:00
Kenneth Reitz
9e45d201f3 Added Dj Gilcrease to AUTHORS. 2011-02-24 01:38:58 -05:00
Kenneth Reitz
47ecb3662d !+= 2011-02-24 01:30:00 -05:00
Kenneth Reitz
7369e47f18 Added Flavio to AUTHORS 2011-02-24 01:29:51 -05:00
Flavio Percoco Premoli
6f5b64bca6 small workaround for issue #7 2011-02-23 08:49:08 +01:00
Kenneth Reitz
e788ca7ee6 Safer error response handling. 2011-02-21 14:36:41 -05:00
Kenneth Reitz
68dea921e7 Added HACKING file. 2011-02-21 02:25:01 -05:00
digitalxero
f96157d259 Fix a syntax error on py2.5 and pypy 2011-02-19 15:33:24 -05:00
digitalxero
718f45698d Reset copyright on the async module 2011-02-19 15:21:47 -05:00
digitalxero
15761fc126 Made it so people can add their own auth handlers and not just the 4 provided by Python 2011-02-19 15:07:58 -05:00
digitalxero
edfdef2396 Add in a proper AuthManager instead of the list version that was being used.
Added support for all Auth types that python supports
2011-02-19 15:04:56 -05:00
digitalxero
a9e18e9106 Update the README 2011-02-19 11:18:29 -05:00
digitalxero
3b82b06adc Added an async module to make it explicit to the user that they are using async calls. 2011-02-19 11:13:10 -05:00
digitalxero
d1c89333d0 Remove the eventles code, this should live in requests.async or something so the user explicitly requests it
Clean up the code to get rid of the unnecessary AuthObject class
Cleanup the send method
Cleanup the convenience methods by adding a request method that they all use.
2011-02-19 10:51:54 -05:00
Kenneth Reitz
e26570d0d8 Merge branch 'hotfix/date' into develop 2011-02-19 01:30:22 -05:00
Kenneth Reitz
8b3c4da2c7 Merge branch 'hotfix/date' 2011-02-19 01:30:10 -05:00
Kenneth Reitz
5c4aa1f4b7 history date fix 2011-02-19 01:30:03 -05:00
Kenneth Reitz
d2cdefa7df Merge branch 'release/0.2.4' 2011-02-19 01:03:39 -05:00
Kenneth Reitz
720d0b2c38 Version needs to be available externally. 2011-02-19 01:02:13 -05:00
Kenneth Reitz
d2584ff853 Production Ready! Added Python 2.5 to Support list. 2011-02-19 01:02:01 -05:00
Kenneth Reitz
433452f9f1 Simpler setup.py versioning. 2011-02-19 01:01:51 -05:00
Kenneth Reitz
a34e0bea8b Version Bump (v0.2.4) 2011-02-19 01:00:03 -05:00
Kenneth Reitz
f66b35454f v0.2.4 Release information 2011-02-19 00:59:56 -05:00
Kenneth Reitz
b5c2806e8c Roadmap update 2011-02-19 00:43:27 -05:00
Kenneth Reitz
5072399f7a Import fix for python2.5 2011-02-19 00:41:28 -05:00
Kenneth Reitz
736cb70e9a Merge branch 'develop' of github.com:kennethreitz/requests into develop 2011-02-19 00:38:09 -05:00
Kenneth Reitz
8eafb8d3df Added autoauth tests 2011-02-19 00:37:33 -05:00
Kenneth Reitz
e8234a0137 import fix 2011-02-18 16:13:49 -05:00
Kenneth Reitz
fbe0393eb3 cstring io fix for py3 2011-02-18 16:08:26 -05:00
Kenneth Reitz
b82e72eb04 cStringIO fix. 2011-02-18 16:00:34 -05:00
Kenneth Reitz
a7c87edebe screw 2.4 2011-02-18 15:57:50 -05:00
Kenneth Reitz
7e20f32b02 pypy configuration 2011-02-18 04:41:42 -05:00
Kenneth Reitz
4419e7183f no more test suite 2011-02-18 04:41:34 -05:00
Kenneth Reitz
6bde955170 Python 2.5 Fixes. 2011-02-18 04:39:53 -05:00
Kenneth Reitz
952970f403 Merge branch 'release/0.9.4' 2011-02-18 03:35:18 -05:00
Kenneth Reitz
d3911da653 tox update (usable now?) 2011-02-18 03:32:10 -05:00
Kenneth Reitz
7b6ed48a86 tox setup for 2.4-2.7 2011-02-17 19:38:40 -05:00
Kenneth Reitz
7229dbe033 Making everyone else happy. 2011-02-17 16:22:03 -05:00
Kenneth Reitz
015d6b78d5 cleaning up line lengths 2011-02-17 16:20:53 -05:00
Kenneth Reitz
f4c4bf3d0b Much nicer helper functions and constructors 2011-02-17 16:17:46 -05:00
Kenneth Reitz
efedbd9775 added test_suite.sh for CI 2011-02-17 13:14:03 -05:00
Kenneth Reitz
f4e03a5d32 gitignore update for jenkins 2011-02-17 13:13:52 -05:00
Kenneth Reitz
34ca692c9c from __future__ import absolute_imports 2011-02-16 12:47:58 -05:00
Kenneth Reitz
a5517960ea history indent fix for rest/pypi 2011-02-15 10:02:39 -05:00
Kenneth Reitz
fa42c12e5b I need autodoc. 2011-02-15 09:58:02 -05:00
Kenneth Reitz
8ac29b40e9 new documentation on Request changes. 2011-02-15 09:51:46 -05:00
Kenneth Reitz
adb53c2b4f Merge branch 'release/0.2.3' into develop 2011-02-15 09:46:58 -05:00
Kenneth Reitz
53e785f656 Merge branch 'release/0.2.3' 2011-02-15 09:46:55 -05:00
Kenneth Reitz
d5256802d4 v0.2.3 2011-02-15 09:46:46 -05:00
Kenneth Reitz
933cf49daa v0.2.3 2011-02-15 09:45:54 -05:00
Kenneth Reitz
83a9bcf1a1 Updating AUTHORS 2011-02-15 09:44:52 -05:00
Kenneth Reitz
4ad73c3655 leave room for _ 2011-02-15 09:42:05 -05:00
Kenneth Reitz
7ad08c5eda New tests for new HTTP STATUS Handling 2011-02-15 09:37:21 -05:00
Kenneth Reitz
4f1301f6ad Now Requests has it's own HTTPError 2011-02-15 09:36:34 -05:00
Kenneth Reitz
6d8a531e87 HTTPError changes are going down in history 2011-02-15 09:26:44 -05:00
Kenneth Reitz
748633d616 raise_for_status is better 2011-02-15 09:26:07 -05:00
Kenneth Reitz
ce122cc811 None, not False 2011-02-15 09:22:11 -05:00
Kenneth Reitz
c900b4379b Added new HTTPError handing mechanisms. 2011-02-15 09:18:43 -05:00
Kenneth Reitz
d6fa43fba7 Merge branch 'release/0.2.2' into develop 2011-02-14 12:59:22 -05:00
Kenneth Reitz
ed909eaac7 Merge branch 'release/0.2.2' 2011-02-14 12:59:06 -05:00
Kenneth Reitz
d511e6f148 Version Bump v0.2.2 2011-02-14 12:58:20 -05:00
Kenneth Reitz
8ff8e02ace Merge branch 'feature/cookies' into develop 2011-02-14 12:52:50 -05:00
Kenneth Reitz
98c8a78c3e Added Cookies documentation. 2011-02-14 12:52:15 -05:00
Kenneth Reitz
397bd4b676 spelling error 2011-02-14 12:45:03 -05:00
Kenneth Reitz
f023c81dad cookies system in place. 2011-02-14 12:43:13 -05:00
Kenneth Reitz
5fe24abfb9 Gevent / Eventlet is back! 2011-02-14 12:40:41 -05:00
Kenneth Reitz
16e5119b2a fixed gevent problem 2011-02-14 12:39:13 -05:00
Kenneth Reitz
b09218d5ac temporary 2011-02-14 12:33:12 -05:00
Kenneth Reitz
4452934929 cleanups 2011-02-14 12:25:44 -05:00
Kenneth Reitz
c9b8750597 Upated history 2011-02-14 12:24:03 -05:00
Kenneth Reitz
2203c3bccd Added CookieJar 2011-02-14 12:23:55 -05:00
Kenneth Reitz
cde3b88f3e eventlet > gevent (i think) 2011-02-14 12:07:34 -05:00
Kenneth Reitz
bfe91296cb monkey patchin' (will this suffice?) 2011-02-14 11:57:12 -05:00
Kenneth Reitz
d87bcce6ad Added release 0.2.2 info. 2011-02-14 11:46:04 -05:00
Kenneth Reitz
f70e89a163 eventlet support. 2011-02-14 11:45:50 -05:00
Kenneth Reitz
09740c35e1 typo 2011-02-14 11:31:44 -05:00
Kenneth Reitz
10280c697d Handles request in event of HTTPError. Fixes Issue #2
Building of internal request object refactor
2011-02-14 11:31:06 -05:00
Kenneth Reitz
a82f4de38d Added AUTHORS file. 2011-02-14 10:50:36 -05:00
Kenneth Reitz
44d402c1e6 who stole dem cookies 2011-02-14 10:50:18 -05:00
Kenneth Reitz
e09efc490e Merge branch 'release/0.2.1' 2011-02-14 10:46:40 -05:00
Kenneth Reitz
43790c4d32 updated multipart-upload documentation 2011-02-14 10:43:54 -05:00
Kenneth Reitz
e1659ed7af added setup.py test 2011-02-14 10:37:38 -05:00
Kenneth Reitz
9f9ebecec2 v0.2.1 2011-02-14 10:35:15 -05:00
Kenneth Reitz
02834e353d added simple test fixture for Postbin and posting
both files and data
2011-02-14 10:00:05 -05:00
Kenneth Reitz
324c572b64 renamed multipart_files to files 2011-02-14 09:59:42 -05:00
Kenneth Reitz
e89eba79df added response.url support for 301's and the like. 2011-02-14 09:53:56 -05:00
Kenneth Reitz
ca51bdb16f Merge branch 'feature/multipart-encode' into develop 2011-02-14 09:45:35 -05:00
Kenneth Reitz
3216bda174 Added multipart_files support for PUT 2011-02-14 09:45:12 -05:00
Kenneth Reitz
4c192ec5ac absolute imports 2011-02-14 09:36:14 -05:00
Kenneth Reitz
4c8b428bbd except clause fix for python3 2011-02-14 09:34:36 -05:00
Kenneth Reitz
a16278e85d string formatting supports None. Who knew. 2011-02-14 09:31:10 -05:00
Kenneth Reitz
f7abfa8a14 multipart_files support! 2011-02-14 09:27:57 -05:00
Kenneth Reitz
7dd37d5ea9 proper poster imports 2011-02-14 09:27:47 -05:00
Kenneth Reitz
127bbbb18d Worth a shot 2011-02-14 09:04:41 -05:00
Kenneth Reitz
221bed27dd import poster from site-packages for now 2011-02-14 09:04:35 -05:00
Kenneth Reitz
e6d5e5319e added multipart_file syntax 2011-02-14 09:01:09 -05:00
Kenneth Reitz
df419fa6fa vendorized poster 2011-02-14 08:55:55 -05:00
Kenneth Reitz
8e8604d88b Added NOTICE for Poster vendorization 2011-02-14 08:55:24 -05:00
Kenneth Reitz
d961000b96 missed one 2011-02-14 05:15:47 -05:00
Kenneth Reitz
9ad8589efd ISC 2011-02-14 03:58:26 -05:00
Kenneth Reitz
06b4969383 manifest.in 2011-02-14 02:49:18 -05:00
Kenneth Reitz
19db3ead4c what happened to manifest? 2011-02-14 02:45:09 -05:00
Kenneth Reitz
800b03457b whitespace 2011-02-14 02:42:46 -05:00
Kenneth Reitz
d1dc6aa329 freaking string replace 2011-02-14 02:40:39 -05:00
Kenneth Reitz
f66eafdffd Docstring whitespace 2011-02-14 02:35:01 -05:00
64 changed files with 7026 additions and 546 deletions

9
.gitignore vendored
View File

@@ -1,2 +1,9 @@
.idea
MANIFEST
MANIFEST
coverage.xml
nosetests.xml
pylint.txt
*.pyc
docs/_build
toy.py
.gitignore

41
AUTHORS Normal file
View File

@@ -0,0 +1,41 @@
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 Selier
- Jens Diemer
- Alex <@alopatin>
- Tom Hogans <tomhsx@gmail.com>
- Armin Ronacher
- Shrikant Sharat Kandula
- Mikko Ohtamaa
- Den Shabalin

40
AUTHORS.orig Normal file
View 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

15
HACKING Normal file
View File

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

View File

@@ -1,6 +1,152 @@
History
-------
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
* Bytes data upload Bugfix
0.3.3 (2011-05-12)
++++++++++++++++++
* Request timeouts
* Unicode url-encoded data
* Settings context manager and module
0.3.2 (2011-04-15)
++++++++++++++++++
* Automatic Decompression of GZip Encoded Content
* AutoAuth Support for Tupled HTTP Auth
0.3.1 (2011-04-01)
++++++++++++++++++
* Cookie Changes
* Response.read()
* Poster fix
0.3.0 (2011-02-25)
++++++++++++++++++
* Automatic Authentication API Change
* Smarter Query URL Parameterization
* Allow file uploads and POST data together
* New Authentication Manager System
- Simpler Basic HTTP System
- Supports all build-in urllib2 Auths
- Allows for custom Auth Handlers
0.2.4 (2011-02-19)
++++++++++++++++++
* Python 2.5 Support
* PyPy-c v1.4 Support
* Auto-Authentication tests
* Improved Request object constructor
0.2.3 (2011-02-15)
++++++++++++++++++
* New HTTPHandling Methods
- Reponse.__nonzero__ (false if bad HTTP Status)
- Response.ok (True if expected HTTP Status)
- Response.error (Logged HTTPError if bad HTTP Status)
- Reponse.raise_for_status() (Raises stored HTTPError)
0.2.2 (2011-02-14)
++++++++++++++++++
* Still handles request in the event of an HTTPError. (Issue #2)
* Eventlet and Gevent Monkeypatch support.
* Cookie Support (Issue #1)
0.2.1 (2011-02-14)
++++++++++++++++++
* Added file attribute to POST and PUT requests for multipart-encode file uploads.
* Added Request.url attribute for context and redirects
0.2.0 (2011-02-14)
++++++++++++++++++

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
include README.rst LICENSE HISTORY.rst

12
NOTICE Normal file
View File

@@ -0,0 +1,12 @@
Request includes some vendorized python libraries to ease installation.
Poster License
==============
Copyright (c) 2010 Chris AtLee
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,19 +1,51 @@
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 Basic HTTP Authentication
+ 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
@@ -27,74 +59,24 @@ 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 = requests.AuthObject('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
200
>>> r.headers['content-type']
'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).
GET Requests
>>> request.get(url, params={}, headers={} auth=None)
<request object>
HEAD Requests
>>> request.head(url, params={}, headers={} auth=None)
<request object>
PUT Requests
>>> request.put(url, data='', headers={}, auth=None)
<request object>
POST Requests
>>> request.post(url, data={}, headers={}, auth=None)
<request object>
DELETE Requests
>>> request.delete(url, params={}, headers={}, auth=None)
<request object>
**Responses:**
Request.status_code:
(Integer) Received HTTP Status Code Response
Request.headers:
(Dictionary) Received HTTP Response Headers
Request.content:
(Bytes) Received Content
**HTTP Authentication Registry:**
You can register AuthObjects to automatically enable HTTP Authentication on requests that contain a registered base URL string.
>>> requests.add_autoauth(url, authobject)
'{"authenticated": true, "user": "user"}'
Installation
@@ -103,13 +85,13 @@ Installation
To install requests, simply: ::
$ pip install requests
Or, if you absolutely must: ::
$ easy_install requests
But, you really shouldn't do that.
Contribute
@@ -118,12 +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
- Exhaustive Unittests
.. _`the repository`: http://github.com/kennethreitz/requests
.. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS

5
debian/changelog vendored Normal file
View 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
View File

@@ -0,0 +1 @@
5

13
debian/control vendored Normal file
View 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
View File

@@ -0,0 +1 @@
docs/user/*.rst

1
debian/pyversions vendored Normal file
View File

@@ -0,0 +1 @@
2.4-

42
debian/rules vendored Executable file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

27
docs/_templates/sidebarintro.html vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,3 @@
*.pyc
*.pyo
.DS_Store

45
docs/_themes/LICENSE vendored Normal file
View 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
View 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
View 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
View 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">
&copy; 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
View 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
View 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
View 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
View 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
View 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 %}

View 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
View 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 = ''

57
docs/api.rst Normal file
View File

@@ -0,0 +1,57 @@
.. _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 6 methods. They
all return a :class:`Response <models.Response>` object.
.. autofunction:: head
.. autofunction:: get
.. autofunction:: post
.. autofunction:: put
.. autofunction:: patch
.. autofunction:: delete
.. autofunction:: request
-----------
.. autoclass:: requests.models.Response
:inherited-members:
Exceptions
----------
.. autoexception:: HTTPError
.. autoexception:: RequestException
.. autoexception:: requests.models.AuthenticationError
.. autoexception:: requests.models.URLRequired
.. autoexception:: requests.models.InvalidMethod
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.
Classes
~~~~~~~
.. autoclass:: requests.models.Request
:inherited-members:

43
docs/community/faq.rst Normal file
View File

@@ -0,0 +1,43 @@
.. _faq:
Frequently Asked Questions
==========================
This part of the documentation covers common questions about Requests.
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 `supported interpreters <interpreters>`_.
Keep-alive Support?
-------------------
It's on the way.

View 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.

View File

@@ -0,0 +1,31 @@
.. _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>`_.

View File

@@ -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 -----------------------------------------------------
@@ -25,7 +27,7 @@ import sys, os
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.jsmath', 'sphinx.ext.viewcode']
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -41,16 +43,16 @@ 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.2.0'
version = __version__
# The full version, including alpha/beta/rc tags.
release = '0.2.0'
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -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
View File

@@ -0,0 +1,5 @@
Authors
=======
.. include:: ../../AUTHORS

0
docs/dev/internals.rst Normal file
View File

15
docs/dev/todo.rst Normal file
View 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>`_)

View File

@@ -3,18 +3,110 @@
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 shouldnt 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.
Testimonals
-----------
`Twitter, Inc <http://twitter.com>`_ uses Requests internally.
**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.
**Steve Pike**
I can never remember how to do it the regular way.
``import requests; requests.get()`` is just so easy!
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

137
docs/user/advanced.rst Normal file
View File

@@ -0,0 +1,137 @@
.. _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).
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
paramaeter::
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]>

53
docs/user/install.rst Normal file
View File

@@ -0,0 +1,53 @@
.. _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

64
docs/user/intro.rst Normal file
View File

@@ -0,0 +1,64 @@
.. _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.
.. _interpreters:
Python Interpreters
-------------------
At this time, the following Python platforms 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 planned.

134
docs/user/quickstart.rst Normal file
View File

@@ -0,0 +1,134 @@
.. _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 handing::
>>> 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

File diff suppressed because one or more lines are too long

View File

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

184
requests/api.py Normal file
View File

@@ -0,0 +1,184 @@
# -*- coding: utf-8 -*-
"""
requests.api
~~~~~~~~~~~~
This module impliments the Requests API.
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
import config
from .models import Request, Response, AuthObject
from .status_codes import codes
from .hooks import dispatch_hook
from .utils import cookiejar_from_dict
from urlparse import urlparse
__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):
"""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 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) 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.
"""
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,
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)
# 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, **kwargs):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
: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) 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 proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('GET', url, **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 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) 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 proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('HEAD', url, **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 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('POST', url, data=data, **kwargs)
def put(url, data='', **kwargs):
"""Sends a PUT 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('PUT', url, data=data, **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 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) 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, **kwargs)

41
requests/async.py Normal file
View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""
requests.async
~~~~~~~~~~~~~~
This module implements the main Requests system, after monkey-patching
the urllib2 module with eventlet or gevent..
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
from __future__ import absolute_import
import urllib
import urllib2
from urllib2 import HTTPError
try:
import eventlet
eventlet.monkey_patch()
except ImportError:
pass
if not 'eventlet' in locals():
try:
from gevent import monkey
monkey.patch_all()
except ImportError:
pass
if not 'eventlet' in locals():
raise ImportError('No Async adaptations of urllib2 found!')
from .core import *

67
requests/config.py Normal file
View 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

View File

@@ -1,405 +1,27 @@
# -*- coding: utf-8 -*-
"""
requests.core
~~~~~~~~~~~~~
requests.core
~~~~~~~~~~~~~
This module implements the main Requests system.
This module implements the main Requests system.
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
import urllib
import urllib2
__title__ = 'requests'
__version__ = '0.2.0'
__build__ = 0x000200
__version__ = '0.6.1'
__build__ = 0x000601
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
AUTOAUTHS = []
class _Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
setting of HTTP methods.
"""
def __init__(self, url,
data=None, headers={}, origin_req_host=None,
unverifiable=False, method=None):
urllib2.Request.__init__( self, url, data, headers, origin_req_host,
unverifiable)
self.method = method
def get_method(self):
if self.method:
return self.method
return urllib2.Request.get_method(self)
class Request(object):
"""The :class:`Request` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
def __init__(self):
self.url = None
self.headers = dict()
self.method = None
self.params = {}
self.data = {}
self.response = Response()
self.auth = None
self.sent = False
def __repr__(self):
try:
repr = '<Request [%s]>' % (self.method)
except:
repr = '<Request object>'
return repr
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 consistiency."""
if not self.url:
raise URLRequired
def _get_opener(self):
""" Creates appropriate opener object for urllib2.
"""
if self.auth:
# create a password manager
authr = urllib2.HTTPPasswordMgrWithDefaultRealm()
authr.add_password(None, self.url, self.auth.username, self.auth.password)
handler = urllib2.HTTPBasicAuthHandler(authr)
opener = urllib2.build_opener(handler)
# use the opener to fetch a URL
return opener.open
else:
return urllib2.urlopen
def send(self, anyway=False):
"""Sends the request. Returns True of successfull, false if not.
If there was an HTTPError during transmission,
self.response.status_code will contain the HTTPError code.
Once a request is successfully sent, `sent` will equal True.
:param anyway: If True, request will be sent, even if it has
already been sent.
"""
self._checks()
success = False
if self.method in ('GET', 'HEAD', 'DELETE'):
if (not self.sent) or anyway:
# url encode GET params if it's a dict
if isinstance(self.params, dict):
params = urllib.urlencode(self.params)
else:
params = self.params
req = _Request(("%s?%s" % (self.url, params)), method=self.method)
if self.headers:
req.headers = self.headers
opener = self._get_opener()
try:
resp = opener(req)
self.response.status_code = resp.code
self.response.headers = resp.info().dict
if self.method.lower() == 'get':
self.response.content = resp.read()
success = True
except urllib2.HTTPError, why:
self.response.status_code = why.code
elif self.method == 'PUT':
if (not self.sent) or anyway:
req = _Request(self.url, method='PUT')
if self.headers:
req.headers = self.headers
req.data = self.data
try:
opener = self._get_opener()
resp = opener(req)
self.response.status_code = resp.code
self.response.headers = resp.info().dict
self.response.content = resp.read()
success = True
except urllib2.HTTPError, why:
self.response.status_code = why.code
elif self.method == 'POST':
if (not self.sent) or anyway:
req = _Request(self.url, method='POST')
if self.headers:
req.headers = self.headers
# url encode form data if it's a dict
if isinstance(self.data, dict):
req.data = urllib.urlencode(self.data)
else:
req.data = self.data
try:
opener = self._get_opener()
resp = opener(req)
self.response.status_code = resp.code
self.response.headers = resp.info().dict
self.response.content = resp.read()
success = True
except urllib2.HTTPError, why:
self.response.status_code = why.code
self.sent = True if success else False
return success
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.
"""
def __init__(self):
self.content = None
self.status_code = None
self.headers = dict()
def __repr__(self):
try:
repr = '<Response [%s]>' % (self.status_code)
except:
repr = '<Response object>'
return repr
class AuthObject(object):
"""The :class:`AuthObject` is a simple HTTP Authentication token. When
given to a Requests function, it enables Basic HTTP Authentication for that
Request. You can also enable Authorization for domain realms with AutoAuth.
See AutoAuth for more details.s
:param username: Username to authenticate with.
:param password: Password for given username.
"""
def __init__(self, username, password):
self.username = username
self.password = password
def get(url, params={}, headers={}, auth=None):
"""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 headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request()
r.method = 'GET'
r.url = url
r.params = params
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
def head(url, params={}, headers={}, auth=None):
"""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 headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request()
r.method = 'HEAD'
r.url = url
# return response object
r.params = params
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
def post(url, data={}, headers={}, auth=None):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary of POST Data to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request()
r.url = url
r.method = 'POST'
r.data = data
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
def put(url, data='', headers={}, auth=None):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Bytes of PUT Data to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request()
r.url = url
r.method = 'PUT'
r.data = data
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
def delete(url, params={}, headers={}, auth=None):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
"""
r = Request()
r.url = url
r.method = 'DELETE'
# return response object
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
def add_autoauth(url, authobject):
"""Registers given AuthObject to given URL domain. for auto-activation.
Once a URL is registered with an AuthObject, the configured HTTP
Authentication will be used for all requests with URLS containing the given
URL string.
Example: ::
>>> c_auth = requests.AuthObject('kennethreitz', 'xxxxxxx')
>>> requests.add_autoauth('https://convore.com/api/', c_auth)
>>> r = requests.get('https://convore.com/api/account/verify.json')
# Automatically HTTP Authenticated! Wh00t!
:param url: Base URL for given AuthObject to auto-activate for.
:param authobject: AuthObject to auto-activate.
"""
global AUTOAUTHS
AUTOAUTHS.append((url, authobject))
def _detect_auth(url, auth):
"""Returns registered AuthObject for given url if available, defaulting to
given AuthObject."""
return _get_autoauth(url) if not auth else auth
def _get_autoauth(url):
"""Returns registered AuthObject for given url if available.
"""
for (autoauth_url, auth) in AUTOAUTHS:
if autoauth_url in url:
return auth
return None
class RequestException(Exception):
"""There was an ambiguous exception that occured while handling your request."""
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."""
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
View 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
View 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

623
requests/models.py Normal file
View File

@@ -0,0 +1,623 @@
# -*- coding: utf-8 -*-
"""
requests.models
~~~~~~~~~~~~~~~
"""
import urllib
import urllib2
import socket
import zlib
from urllib2 import HTTPError
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 <models.Request>` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
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):
#: 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
#: Dictionary or byte of request body data to attach to the
#: :class:`Request <models.Request>`.
self.data = None
#: 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
# 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 _checks(self):
"""Deterministic checks for consistency."""
if not self.url:
raise URLRequired
def _get_opener(self):
"""Creates appropriate opener object for urllib2."""
_handlers = []
if self.cookiejar is not None:
_handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar))
if self.auth:
if not isinstance(self.auth.handler, (urllib2.AbstractBasicAuthHandler, urllib2.AbstractDigestAuthHandler)):
# 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
if self.data or self.files:
_handlers.extend(get_handlers())
opener = urllib2.build_opener(*_handlers)
if self.headers:
# Allow default headers in the opener to be overloaded
normal_keys = [k.capitalize() for k in self.headers]
for key, val in opener.addheaders[:]:
if key not in normal_keys:
continue
# Remove it, we have a value to take its place
opener.addheaders.remove((key, val))
return opener.open
def _build_response(self, resp, is_error=False):
"""Build internal :class:`Response <models.Response>` object from given response."""
def build(resp):
response = Response()
response.status_code = getattr(resp, 'code', None)
try:
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
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
((self.method in ('GET', 'HEAD')) or
(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 _encode_params(data):
"""Encode parameters in a piece of 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:
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 '%s?%s' % (self.url, self._enc_params)
else:
return self.url
def send(self, anyway=False):
"""Sends the request. Returns True of successful, false if not.
If there was an HTTPError during transmission,
self.response.status_code will contain the HTTPError code.
Once a request is successfully sent, `sent` will equal True.
: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(url, method=self.method)
else:
if self.files:
register_openers()
if self.data:
self.files.update(self.data)
datagen, headers = multipart_encode(self.files)
req = _Request(url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(url, data=self._enc_data, method=self.method)
if 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()
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, urllib2.URLError), why:
if hasattr(why, 'reason'):
if isinstance(why.reason, socket.timeout):
why = Timeout(why)
self._build_response(why, is_error=True)
else:
self._build_response(resp)
self.response.ok = True
self.sent = self.response.ok
return self.sent
class Response(object):
"""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):
#: 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
#: 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
#: 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):
return '<Response [%s]>' % (self.status_code)
def __nonzero__(self):
"""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 :class:`HTTPError` or :class:`URLError`, if one occured."""
if self.error:
raise self.error
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):
"""Requests Authentication Manager."""
def __new__(cls):
singleton = cls.__dict__.get('__singleton__')
if singleton is not None:
return singleton
cls.__singleton__ = singleton = object.__new__(cls)
return singleton
def __init__(self):
self.passwd = {}
self._auth = {}
def __repr__(self):
return '<AuthManager [%s]>' % (self.method)
def add_auth(self, uri, auth):
"""Registers AuthObject to AuthManager."""
uri = self.reduce_uri(uri, False)
# try to make it an AuthObject
if not isinstance(auth, AuthObject):
try:
auth = AuthObject(*auth)
except TypeError:
pass
self._auth[uri] = auth
def add_password(self, realm, uri, user, passwd):
"""Adds password to AuthManager."""
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
reduced_uri = tuple([self.reduce_uri(u, False) for u in uri])
if reduced_uri not in self.passwd:
self.passwd[reduced_uri] = {}
self.passwd[reduced_uri] = (user, passwd)
def find_user_password(self, realm, authuri):
for uris, authinfo in self.passwd.iteritems():
reduced_authuri = self.reduce_uri(authuri, False)
for uri in uris:
if self.is_suburi(uri, reduced_authuri):
return authinfo
return (None, None)
def get_auth(self, uri):
(in_domain, in_path) = self.reduce_uri(uri, False)
for domain, path, authority in (
(i[0][0], i[0][1], i[1]) for i in self._auth.iteritems()
):
if in_domain == domain:
if path in in_path:
return authority
def reduce_uri(self, uri, default_port=True):
"""Accept authority or URI and extract only the authority and path."""
# note HTTP URLs do not have a userinfo component
parts = urllib2.urlparse.urlsplit(uri)
if parts[1]:
# URI
scheme = parts[0]
authority = parts[1]
path = parts[2] or '/'
else:
# host or host:port
scheme = None
authority = uri
path = '/'
host, port = urllib2.splitport(authority)
if default_port and port is None and scheme is not None:
dport = {"http": 80,
"https": 443,
}.get(scheme)
if dport is not None:
authority = "%s:%d" % (host, dport)
return authority, path
def is_suburi(self, base, test):
"""Check if test is below base in a URI tree
Both args must be URIs in reduced form.
"""
if base == test:
return True
if base[0] != test[0]:
return False
common = urllib2.posixpath.commonprefix((base[1], test[1]))
if len(common) == len(base[1]):
return True
return False
def empty(self):
self.passwd = {}
def remove(self, uri, realm=None):
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
for default_port in True, False:
reduced_uri = tuple([self.reduce_uri(u, default_port) for u in uri])
del self.passwd[reduced_uri][realm]
def __contains__(self, uri):
# uri could be a single URI or a sequence
if isinstance(uri, basestring):
uri = [uri]
uri = tuple([self.reduce_uri(u, False) for u in uri])
if uri in self.passwd:
return True
return False
auth_manager = AuthManager()
class AuthObject(object):
"""The :class:`AuthObject` is a simple HTTP Authentication token. When
given to a Requests function, it enables Basic HTTP Authentication for that
Request. You can also enable Authorization for domain realms with AutoAuth.
See AutoAuth for more details.
:param username: Username to authenticate with.
:param password: Password for given username.
:param realm: (optional) the realm this auth applies to
:param handler: (optional) basic || digest || proxy_basic || proxy_digest
"""
_handlers = {
'basic': HTTPBasicAuthHandler,
'forced_basic': HTTPForcedBasicAuthHandler,
'digest': HTTPDigestAuthHandler,
'proxy_basic': urllib2.ProxyBasicAuthHandler,
'proxy_digest': urllib2.ProxyDigestAuthHandler
}
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(), HTTPForcedBasicAuthHandler)
else:
self.handler = handler

148
requests/monkeys.py Normal file
View File

@@ -0,0 +1,148 @@
#-*- coding: utf-8 -*-
"""
requests.monkeys
~~~~~~~~~~~~~~~~
Urllib2 Monkey patches.
"""
import urllib2
import re
class Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
setting of HTTP methods.
"""
def __init__(self, url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None):
urllib2.Request.__init__(self, url, data, headers, origin_req_host, unverifiable)
self.method = method
def get_method(self):
if self.method:
return self.method
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):
"""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
# forever. We disable reset_retry_count completely and reset in
# 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
)
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
class HTTPDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
def __init__(self, *args, **kwargs):
urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
self.retried_req = None
def reset_retry_count(self):
# Python 2.6.5 will call this on 401 or 407 errors and thus loop
# forever. We disable reset_retry_count completely and reset in
# 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
# In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
# it doesn't know about the auth type requested. This can happen if
# somebody is using BasicAuth and types a bad password.
try:
return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
except ValueError, inst:
arg = inst.args[0]
if arg.startswith("AbstractDigestAuthHandler doesn't know "):
return
raise

View File

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

View File

@@ -0,0 +1,34 @@
# Copyright (c) 2010 Chris AtLee
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""poster module
Support for streaming HTTP uploads, and multipart/form-data encoding
```poster.version``` is a 3-tuple of integers representing the version number.
New releases of poster will always have a version number that compares greater
than an older version of poster.
New in version 0.6."""
from __future__ import absolute_import
from . import streaminghttp
from . import encode
version = (0, 8, 0) # Thanks JP!

View File

@@ -0,0 +1,414 @@
"""multipart/form-data encoding module
This module provides functions that faciliate encoding name/value pairs
as multipart/form-data suitable for a HTTP POST or PUT request.
multipart/form-data is the standard way to upload files over HTTP"""
__all__ = ['gen_boundary', 'encode_and_quote', 'MultipartParam',
'encode_string', 'encode_file_header', 'get_body_size', 'get_headers',
'multipart_encode']
try:
import uuid
def gen_boundary():
"""Returns a random string to use as the boundary for a message"""
return uuid.uuid4().hex
except ImportError:
import random, sha
def gen_boundary():
"""Returns a random string to use as the boundary for a message"""
bits = random.getrandbits(160)
return sha.new(str(bits)).hexdigest()
import urllib, re, os, mimetypes
try:
from email.header import Header
except ImportError:
# Python 2.4
from email.Header import Header
def encode_and_quote(data):
"""If ``data`` is unicode, return urllib.quote_plus(data.encode("utf-8"))
otherwise return urllib.quote_plus(data)"""
if data is None:
return None
if isinstance(data, unicode):
data = data.encode("utf-8")
return urllib.quote_plus(data)
def _strify(s):
"""If s is a unicode string, encode it to UTF-8 and return the results,
otherwise return str(s), or None if s is None"""
if s is None:
return None
if isinstance(s, unicode):
return s.encode("utf-8")
return str(s)
class MultipartParam(object):
"""Represents a single parameter in a multipart/form-data request
``name`` is the name of this parameter.
If ``value`` is set, it must be a string or unicode object to use as the
data for this parameter.
If ``filename`` is set, it is what to say that this parameter's filename
is. Note that this does not have to be the actual filename any local file.
If ``filetype`` is set, it is used as the Content-Type for this parameter.
If unset it defaults to "text/plain; charset=utf8"
If ``filesize`` is set, it specifies the length of the file ``fileobj``
If ``fileobj`` is set, it must be a file-like object that supports
.read().
Both ``value`` and ``fileobj`` must not be set, doing so will
raise a ValueError assertion.
If ``fileobj`` is set, and ``filesize`` is not specified, then
the file's size will be determined first by stat'ing ``fileobj``'s
file descriptor, and if that fails, by seeking to the end of the file,
recording the current position as the size, and then by seeking back to the
beginning of the file.
``cb`` is a callable which will be called from iter_encode with (self,
current, total), representing the current parameter, current amount
transferred, and the total size.
"""
def __init__(self, name, value=None, filename=None, filetype=None,
filesize=None, fileobj=None, cb=None):
self.name = Header(name).encode()
self.value = _strify(value)
if filename is None:
self.filename = None
else:
if isinstance(filename, unicode):
# Encode with XML entities
self.filename = filename.encode("ascii", "xmlcharrefreplace")
else:
self.filename = str(filename)
self.filename = self.filename.encode("string_escape").\
replace('"', '\\"')
self.filetype = _strify(filetype)
self.filesize = filesize
self.fileobj = fileobj
self.cb = cb
if self.value is not None and self.fileobj is not None:
raise ValueError("Only one of value or fileobj may be specified")
if fileobj is not None and filesize is None:
# Try and determine the file size
try:
self.filesize = os.fstat(fileobj.fileno()).st_size
except (OSError, AttributeError):
try:
fileobj.seek(0, 2)
self.filesize = fileobj.tell()
fileobj.seek(0)
except:
raise ValueError("Could not determine filesize")
def __cmp__(self, other):
attrs = ['name', 'value', 'filename', 'filetype', 'filesize', 'fileobj']
myattrs = [getattr(self, a) for a in attrs]
oattrs = [getattr(other, a) for a in attrs]
return cmp(myattrs, oattrs)
def reset(self):
if self.fileobj is not None:
self.fileobj.seek(0)
elif self.value is None:
raise ValueError("Don't know how to reset this parameter")
@classmethod
def from_file(cls, paramname, filename):
"""Returns a new MultipartParam object constructed from the local
file at ``filename``.
``filesize`` is determined by os.path.getsize(``filename``)
``filetype`` is determined by mimetypes.guess_type(``filename``)[0]
``filename`` is set to os.path.basename(``filename``)
"""
return cls(paramname, filename=os.path.basename(filename),
filetype=mimetypes.guess_type(filename)[0],
filesize=os.path.getsize(filename),
fileobj=open(filename, "rb"))
@classmethod
def from_params(cls, params):
"""Returns a list of MultipartParam objects from a sequence of
name, value pairs, MultipartParam instances,
or from a mapping of names to values
The values may be strings or file objects, or MultipartParam objects.
MultipartParam object names must match the given names in the
name,value pairs or mapping, if applicable."""
if hasattr(params, 'items'):
params = params.items()
retval = []
for item in params:
if isinstance(item, cls):
retval.append(item)
continue
name, value = item
if isinstance(value, cls):
assert value.name == name
retval.append(value)
continue
if hasattr(value, 'read'):
# Looks like a file object
filename = getattr(value, 'name', None)
if filename is not None:
filetype = mimetypes.guess_type(filename)[0]
else:
filetype = None
retval.append(cls(name=name, filename=filename,
filetype=filetype, fileobj=value))
else:
retval.append(cls(name, value))
return retval
def encode_hdr(self, boundary):
"""Returns the header of the encoding of this parameter"""
boundary = encode_and_quote(boundary)
headers = ["--%s" % boundary]
if self.filename:
disposition = 'form-data; name="%s"; filename="%s"' % (self.name,
self.filename)
else:
disposition = 'form-data; name="%s"' % self.name
headers.append("Content-Disposition: %s" % disposition)
if self.filetype:
filetype = self.filetype
else:
filetype = "text/plain; charset=utf-8"
headers.append("Content-Type: %s" % filetype)
headers.append("")
headers.append("")
return "\r\n".join(headers)
def encode(self, boundary):
"""Returns the string encoding of this parameter"""
if self.value is None:
value = self.fileobj.read()
else:
value = self.value
if re.search("^--%s$" % re.escape(boundary), value, re.M):
raise ValueError("boundary found in encoded string")
return "%s%s\r\n" % (self.encode_hdr(boundary), value)
def iter_encode(self, boundary, blocksize=4096):
"""Yields the encoding of this parameter
If self.fileobj is set, then blocks of ``blocksize`` bytes are read and
yielded."""
total = self.get_size(boundary)
current = 0
if self.value is not None:
block = self.encode(boundary)
current += len(block)
yield block
if self.cb:
self.cb(self, current, total)
else:
block = self.encode_hdr(boundary)
current += len(block)
yield block
if self.cb:
self.cb(self, current, total)
last_block = ""
encoded_boundary = "--%s" % encode_and_quote(boundary)
boundary_exp = re.compile("^%s$" % re.escape(encoded_boundary),
re.M)
while True:
block = self.fileobj.read(blocksize)
if not block:
current += 2
yield "\r\n"
if self.cb:
self.cb(self, current, total)
break
last_block += block
if boundary_exp.search(last_block):
raise ValueError("boundary found in file data")
last_block = last_block[-len(encoded_boundary)-2:]
current += len(block)
yield block
if self.cb:
self.cb(self, current, total)
def get_size(self, boundary):
"""Returns the size in bytes that this param will be when encoded
with the given boundary."""
if self.filesize is not None:
valuesize = self.filesize
else:
valuesize = len(self.value)
return len(self.encode_hdr(boundary)) + 2 + valuesize
def encode_string(boundary, name, value):
"""Returns ``name`` and ``value`` encoded as a multipart/form-data
variable. ``boundary`` is the boundary string used throughout
a single request to separate variables."""
return MultipartParam(name, value).encode(boundary)
def encode_file_header(boundary, paramname, filesize, filename=None,
filetype=None):
"""Returns the leading data for a multipart/form-data field that contains
file data.
``boundary`` is the boundary string used throughout a single request to
separate variables.
``paramname`` is the name of the variable in this request.
``filesize`` is the size of the file data.
``filename`` if specified is the filename to give to this field. This
field is only useful to the server for determining the original filename.
``filetype`` if specified is the MIME type of this file.
The actual file data should be sent after this header has been sent.
"""
return MultipartParam(paramname, filesize=filesize, filename=filename,
filetype=filetype).encode_hdr(boundary)
def get_body_size(params, boundary):
"""Returns the number of bytes that the multipart/form-data encoding
of ``params`` will be."""
size = sum(p.get_size(boundary) for p in MultipartParam.from_params(params))
return size + len(boundary) + 6
def get_headers(params, boundary):
"""Returns a dictionary with Content-Type and Content-Length headers
for the multipart/form-data encoding of ``params``."""
headers = {}
boundary = urllib.quote_plus(boundary)
headers['Content-Type'] = "multipart/form-data; boundary=%s" % boundary
headers['Content-Length'] = str(get_body_size(params, boundary))
return headers
class multipart_yielder:
def __init__(self, params, boundary, cb):
self.params = params
self.boundary = boundary
self.cb = cb
self.i = 0
self.p = None
self.param_iter = None
self.current = 0
self.total = get_body_size(params, boundary)
def __iter__(self):
return self
def next(self):
"""generator function to yield multipart/form-data representation
of parameters"""
if self.param_iter is not None:
try:
block = self.param_iter.next()
self.current += len(block)
if self.cb:
self.cb(self.p, self.current, self.total)
return block
except StopIteration:
self.p = None
self.param_iter = None
if self.i is None:
raise StopIteration
elif self.i >= len(self.params):
self.param_iter = None
self.p = None
self.i = None
block = "--%s--\r\n" % self.boundary
self.current += len(block)
if self.cb:
self.cb(self.p, self.current, self.total)
return block
self.p = self.params[self.i]
self.param_iter = self.p.iter_encode(self.boundary)
self.i += 1
return self.next()
def reset(self):
self.i = 0
self.current = 0
for param in self.params:
param.reset()
def multipart_encode(params, boundary=None, cb=None):
"""Encode ``params`` as multipart/form-data.
``params`` should be a sequence of (name, value) pairs or MultipartParam
objects, or a mapping of names to values.
Values are either strings parameter values, or file-like objects to use as
the parameter value. The file-like objects must support .read() and either
.fileno() or both .seek() and .tell().
If ``boundary`` is set, then it as used as the MIME boundary. Otherwise
a randomly generated boundary will be used. In either case, if the
boundary string appears in the parameter values a ValueError will be
raised.
If ``cb`` is set, it should be a callback which will get called as blocks
of data are encoded. It will be called with (param, current, total),
indicating the current parameter being encoded, the current amount encoded,
and the total amount to encode.
Returns a tuple of `datagen`, `headers`, where `datagen` is a
generator that will yield blocks of data that make up the encoded
parameters, and `headers` is a dictionary with the assoicated
Content-Type and Content-Length headers.
Examples:
>>> datagen, headers = multipart_encode( [("key", "value1"), ("key", "value2")] )
>>> s = "".join(datagen)
>>> assert "value2" in s and "value1" in s
>>> p = MultipartParam("key", "value2")
>>> datagen, headers = multipart_encode( [("key", "value1"), p] )
>>> s = "".join(datagen)
>>> assert "value2" in s and "value1" in s
>>> datagen, headers = multipart_encode( {"key": "value1"} )
>>> s = "".join(datagen)
>>> assert "value2" not in s and "value1" in s
"""
if boundary is None:
boundary = gen_boundary()
else:
boundary = urllib.quote_plus(boundary)
headers = get_headers(params, boundary)
params = MultipartParam.from_params(params)
return multipart_yielder(params, boundary, cb), headers

View File

@@ -0,0 +1,199 @@
"""Streaming HTTP uploads module.
This module extends the standard httplib and urllib2 objects so that
iterable objects can be used in the body of HTTP requests.
In most cases all one should have to do is call :func:`register_openers()`
to register the new streaming http handlers which will take priority over
the default handlers, and then you can use iterable objects in the body
of HTTP requests.
**N.B.** You must specify a Content-Length header if using an iterable object
since there is no way to determine in advance the total size that will be
yielded, and there is no way to reset an interator.
Example usage:
>>> from StringIO import StringIO
>>> import urllib2, poster.streaminghttp
>>> opener = poster.streaminghttp.register_openers()
>>> s = "Test file data"
>>> f = StringIO(s)
>>> req = urllib2.Request("http://localhost:5000", f,
... {'Content-Length': str(len(s))})
"""
import httplib, urllib2, socket
from httplib import NotConnected
__all__ = ['StreamingHTTPConnection', 'StreamingHTTPRedirectHandler',
'StreamingHTTPHandler', 'register_openers']
if hasattr(httplib, 'HTTPS'):
__all__.extend(['StreamingHTTPSHandler', 'StreamingHTTPSConnection'])
class _StreamingHTTPMixin:
"""Mixin class for HTTP and HTTPS connections that implements a streaming
send method."""
def send(self, value):
"""Send ``value`` to the server.
``value`` can be a string object, a file-like object that supports
a .read() method, or an iterable object that supports a .next()
method.
"""
# Based on python 2.6's httplib.HTTPConnection.send()
if self.sock is None:
if self.auto_open:
self.connect()
else:
raise NotConnected()
# send the data to the server. if we get a broken pipe, then close
# the socket. we want to reconnect when somebody tries to send again.
#
# NOTE: we DO propagate the error, though, because we cannot simply
# ignore the error... the caller will know if they can retry.
if self.debuglevel > 0:
print "send:", repr(value)
try:
blocksize = 8192
if hasattr(value, 'read') :
if hasattr(value, 'seek'):
value.seek(0)
if self.debuglevel > 0:
print "sendIng a read()able"
data = value.read(blocksize)
while data:
self.sock.sendall(data)
data = value.read(blocksize)
elif hasattr(value, 'next'):
if hasattr(value, 'reset'):
value.reset()
if self.debuglevel > 0:
print "sendIng an iterable"
for data in value:
self.sock.sendall(data)
else:
self.sock.sendall(value)
except socket.error, v:
if v[0] == 32: # Broken pipe
self.close()
raise
class StreamingHTTPConnection(_StreamingHTTPMixin, httplib.HTTPConnection):
"""Subclass of `httplib.HTTPConnection` that overrides the `send()` method
to support iterable body objects"""
class StreamingHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
"""Subclass of `urllib2.HTTPRedirectHandler` that overrides the
`redirect_request` method to properly handle redirected POST requests
This class is required because python 2.5's HTTPRedirectHandler does
not remove the Content-Type or Content-Length headers when requesting
the new resource, but the body of the original request is not preserved.
"""
handler_order = urllib2.HTTPRedirectHandler.handler_order - 1
# From python2.6 urllib2's HTTPRedirectHandler
def redirect_request(self, req, fp, code, msg, headers, newurl):
"""Return a Request or None in response to a redirect.
This is called by the http_error_30x methods when a
redirection response is received. If a redirection should
take place, return a new Request to allow http_error_30x to
perform the redirect. Otherwise, raise HTTPError if no-one
else should try to handle this url. Return None if you can't
but another Handler might.
"""
m = req.get_method()
if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
or code in (301, 302, 303) and m == "POST"):
# Strictly (according to RFC 2616), 301 or 302 in response
# to a POST MUST NOT cause a redirection without confirmation
# from the user (of urllib2, in this case). In practice,
# essentially all clients do redirect in this case, so we
# do the same.
# be conciliant with URIs containing a space
newurl = newurl.replace(' ', '%20')
newheaders = dict((k, v) for k, v in req.headers.items()
if k.lower() not in (
"content-length", "content-type")
)
return urllib2.Request(newurl,
headers=newheaders,
origin_req_host=req.get_origin_req_host(),
unverifiable=True)
else:
raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
class StreamingHTTPHandler(urllib2.HTTPHandler):
"""Subclass of `urllib2.HTTPHandler` that uses
StreamingHTTPConnection as its http connection class."""
handler_order = urllib2.HTTPHandler.handler_order - 1
def http_open(self, req):
"""Open a StreamingHTTPConnection for the given request"""
return self.do_open(StreamingHTTPConnection, req)
def http_request(self, req):
"""Handle a HTTP request. Make sure that Content-Length is specified
if we're using an interable value"""
# Make sure that if we're using an iterable object as the request
# body, that we've also specified Content-Length
if req.has_data():
data = req.get_data()
if hasattr(data, 'read') or hasattr(data, 'next'):
if not req.has_header('Content-length'):
raise ValueError(
"No Content-Length specified for iterable body")
return urllib2.HTTPHandler.do_request_(self, req)
if hasattr(httplib, 'HTTPS'):
class StreamingHTTPSConnection(_StreamingHTTPMixin,
httplib.HTTPSConnection):
"""Subclass of `httplib.HTTSConnection` that overrides the `send()`
method to support iterable body objects"""
class StreamingHTTPSHandler(urllib2.HTTPSHandler):
"""Subclass of `urllib2.HTTPSHandler` that uses
StreamingHTTPSConnection as its http connection class."""
handler_order = urllib2.HTTPSHandler.handler_order - 1
def https_open(self, req):
return self.do_open(StreamingHTTPSConnection, req)
def https_request(self, req):
# Make sure that if we're using an iterable object as the request
# body, that we've also specified Content-Length
if req.has_data():
data = req.get_data()
if hasattr(data, 'read') or hasattr(data, 'next'):
if not req.has_header('Content-length'):
raise ValueError(
"No Content-Length specified for iterable body")
return urllib2.HTTPSHandler.do_request_(self, req)
def get_handlers():
handlers = [StreamingHTTPHandler, StreamingHTTPRedirectHandler]
if hasattr(httplib, "HTTPS"):
handlers.append(StreamingHTTPSHandler)
return handlers
def register_openers():
"""Register the streaming http handlers in the global urllib2 default
opener object.
Returns the created OpenerDirector object."""
opener = urllib2.build_opener(*get_handlers())
urllib2.install_opener(opener)
return opener

5
requests/patches.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
"""
requests.monkeys
"""

84
requests/sessions.py Normal file
View 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
View 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)

65
requests/structures.py Normal file
View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
"""
requests.structures
~~~~~~~~~~~~~~~~~~~
Datastructures that power Requests.
"""
class CaseInsensitiveDict(dict):
"""Case-insensitive Dictionary
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
def _clear_lower_keys(self):
if hasattr(self, '_lower_keys'):
self._lower_keys.clear()
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self._clear_lower_keys()
def __delitem__(self, key):
dict.__delitem__(self, key)
self._lower_keys.clear()
def __contains__(self, key):
return key.lower() in self.lower_keys
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()])
def get(self, key, default=None):
if key in self:
return self[key]
else:
return default
class LookupDict(dict):
"""Dictionary lookup object."""
def __init__(self, name=None):
self.name = name
super(LookupDict, self).__init__()
def __repr__(self):
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
View 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

70
setup.py Normal file → Executable file
View File

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

484
test_requests.py Normal file → Executable file
View File

@@ -1,47 +1,475 @@
#!/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.org/'
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."""
def setUp(self):
pass
"""Requests test cases."""
def tearDown(self):
"""Teardown."""
pass
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get('http://google.com')
self.assertEqual(r.status_code, 200)
def setUp(self):
pass
def test_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
self.assertEqual(r.status_code, 200)
def tearDown(self):
"""Teardown."""
pass
def test_HTTPS_200_OK_HEAD(self):
r = requests.head('https://google.com')
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTPS_200_OK_GET(self):
auth = requests.AuthObject('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
r = requests.get(url, auth=auth)
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_GET(self):
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(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(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(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(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_HEAD(self):
r = requests.head(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_PUT(self):
r = requests.put(httpbin('put'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_PUT(self):
r = requests.put(httpsbin('put'))
self.assertEqual(r.status_code, 200)
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):
for service in SERVICES:
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)
def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
for service in SERVICES:
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):
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)
def test_nonzero_evaluation(self):
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):
for service in SERVICES:
r = requests.get(service('status', '404'))
self.assertEqual(r.ok, False)
def test_status_raising(self):
r = requests.get(httpbin('status', '404'))
self.assertRaises(requests.HTTPError, r.raise_for_status)
r = requests.get(httpbin('status', '200'))
self.assertFalse(r.error)
r.raise_for_status()
def test_cookie_jar(self):
jar = cookielib.CookieJar()
self.assertFalse(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(httpbin('gzip'))
r.content.decode('ascii')
def test_unicode_get(self):
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):
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_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)
self.assertEqual(r.status_code, 200)
if __name__ == '__main__':
unittest.main()
unittest.main()

8
tox.ini Normal file
View File

@@ -0,0 +1,8 @@
[tox]
envlist = py25,py26,py27
[testenv]
commands=py.test --junitxml=junit-{envname}.xml
deps =
pytest
omnijson