Compare commits

...

342 Commits

Author SHA1 Message Date
Kenneth Reitz
15511584d1 Merge branch 'release/0.6.3' 2011-10-13 21:33:55 -04:00
Kenneth Reitz
80cd1ee663 v0.6.3 history 2011-10-13 21:23:42 -04:00
Kenneth Reitz
3161783e49 history fix 2011-10-13 21:19:47 -04:00
Kenneth Reitz
e9b4defe8d Merge pull request #201 from kennethreitz/feature/async
Beautiful Async Module
2011-10-13 18:15:18 -07:00
Kenneth Reitz
a4b87c6fc1 v0.6.3 history 2011-10-13 21:14:31 -04:00
Kenneth Reitz
cc350d54b7 v0.6.3 2011-10-13 21:13:46 -04:00
Kenneth Reitz
3afd07451c prefetch=False for async.map 2011-10-13 21:10:34 -04:00
Kenneth Reitz
27ee6e0f45 Async requests docs 2011-10-13 21:10:25 -04:00
Kenneth Reitz
849934ccd6 Merge branch 'develop' into feature/async 2011-10-13 20:50:22 -04:00
Kenneth Reitz
597f0ae5fd Catch raw socket errors 2011-10-13 20:47:58 -04:00
Kenneth Reitz
2115046bbc Merge branch 'develop' into feature/async 2011-10-13 20:33:55 -04:00
Kenneth Reitz
565745f7b7 Make it so. 2011-10-13 20:33:39 -04:00
Kenneth Reitz
76b8afee94 Make tests go to 11! 2011-10-13 20:33:34 -04:00
Kenneth Reitz
892fcd6cc0 store hooks in Request. 2011-10-13 20:19:59 -04:00
Kenneth Reitz
5537902796 Remove keep_alive cruft from urllib3 (for now) 2011-10-13 20:17:22 -04:00
Kenneth Reitz
9518d053a4 _return_request => return_response 2011-10-13 20:11:25 -04:00
Kenneth Reitz
93c44157ba initial import of urllib3 branch async 2011-10-13 20:11:05 -04:00
Kenneth Reitz
d47522e6fb v0.6.3 plans 2011-10-13 19:54:59 -04:00
Kenneth Reitz
3e233e4856 v0.6.2 in changelog 2011-10-13 19:53:34 -04:00
Kenneth Reitz
4a7fdef3ea Migrate new docs over. 2011-10-13 14:54:36 -04:00
Kenneth Reitz
98c719f714 Merge branch 'redirect-no-follow' of https://github.com/millerdev/requests 2011-10-09 07:05:14 -04:00
Daniel Miller
dda533cf67 Added myself to AUTHORS file 2011-10-05 15:09:57 -04:00
Daniel Miller
aa04cc9b42 Make get and head requests respect allow_redirects=False. 2011-10-05 14:36:12 -04:00
Kenneth Reitz
3b085ed20f Merge branch 'develop' 2011-08-20 18:26:04 -04:00
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
57 changed files with 5779 additions and 715 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ coverage.xml
nosetests.xml
pylint.txt
*.pyc
docs/_build
toy.py
.gitignore

23
AUTHORS
View File

@@ -18,4 +18,25 @@ Patches and Suggestions
- Rob Madole
- Aram Dulyan
- Johannes Gorset
- 村山めがね (Megan Emurayama)
- 村山めがね (Megane Murayama)
- James Rowe
- Daniel Schauenberg
- Zbigniew Siciarz
- Daniele Tricoli 'Eriol'
- Richard Boulton
- Miguel Olivares <miguel@moliware.com>
- Alberto Paro
- Jérémy Bethmont
- 潘旭 (Xu Pan)
- Tamás Gulácsi
- Rubén Abad
- Peter Manser
- Jeremy Selier
- Jens Diemer
- Alex <@alopatin>
- Tom Hogans <tomhsx@gmail.com>
- Armin Ronacher
- Shrikant Sharat Kandula
- Mikko Ohtamaa
- Den Shabalin
- Daniel Miller <danielm@vs-networks.com>

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

View File

@@ -1,8 +1,89 @@
History
-------
0.3.4
+++++
0.6.3 (2011-10-13)
++++++++++++++++++
* Beautiful ``requests.async`` module, for making async requests w/ gevent.
0.6.2 (2011-10-09)
++++++++++++++++++
* GET/HEAD obeys allow_redirects=False
0.6.1 (2011-08-20)
++++++++++++++++++
* Enhanced status codes experience ``\o/``
* Set a maximum number of redirects (``settings.max_redirects``)
* Full Unicode URL support
* Support for protocol-less redirects.
* Allow for arbitrary request types.
* Bugfixes
0.6.0 (2011-08-17)
++++++++++++++++++
* New callback hook system
* New persistient sessions object and context manager
* Transparent Dict-cookie handling
* Status code reference object
* Removed Response.cached
* Added Response.request
* All args are kwargs
* Relative redirect support
* HTTPError handling improvements
* Improved https testing
* Bugfixes
0.5.1 (2011-07-23)
++++++++++++++++++
* International Domain Name Support!
* Access headers without fetching entire body (``read()``)
* Use lists as dicts for parameters
* Add Forced Basic Authentication
* Forced Basic is default authentication type
* ``python-requests.org`` default User-Agent header
* CaseInsensitiveDict lower-case caching
* Response.history bugfix
0.5.0 (2011-06-21)
++++++++++++++++++
* PATCH Support
* Support for Proxies
* HTTPBin Test Suite
* Redirect Fixes
* settings.verbose stream writing
* Querystrings for all methods
* URLErrors (Connection Refused, Timeout, Invalid URLs) are treated as explicity raised
``r.requests.get('hwe://blah'); r.raise_for_status()``
0.4.1 (2011-05-22)
++++++++++++++++++
* Improved Redirection Handling
* New 'allow_redirects' param for following non-GET/HEAD Redirects
* Settings module refactoring
0.4.0 (2011-05-15)
++++++++++++++++++
* Response.history: list of redirected responses
* Case-Insensitive Header Dictionaries!
* Unicode URLs
0.3.4 (2011-05-14)
++++++++++++++++++
* Urllib2 HTTPAuthentication Recursion fix (Basic/Digest)
* Internal Refactor

14
Makefile Normal file
View File

@@ -0,0 +1,14 @@
init:
pip install -r reqs.txt
test:
nosetests test_requests.py --processes=30
ci: init
nosetests --search-test --processes=30 --with-nosexunit test_requests.py
pyflakes requests | awk -F\: '{printf "%s:%s: [E]%s\n", $1, $2, $3}' > violations.pyflakes.txt
site:
cd docs; make dirhtml
docs: site

View File

@@ -1,20 +1,49 @@
Requests: The Simple (e.g. usable) HTTP Module
==============================================
Requests: HTTP for Humans
=========================
Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging).
Requests is an ISC Licensed HTTP library, written in Python, for human
beings.
But this one's different. This one's going to be awesome. And simple.
Most existing Python modules for sending HTTP requests are extremely
verbose and cumbersome. Python's builtin urllib2 module provides most of
the HTTP capabilities you should need, but the api is thoroughly broken.
It requires an enormous amount of work (even method overrides) to
perform the simplest of tasks.
Things shouldn't be this way. Not in Python.
::
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
>>> r.status_code
204
>>> r.headers['content-type']
'application/json'
>>> r.content
...
See `the same code, without Requests <https://gist.github.com/973705>`_.
Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
**PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by urllib2, but it does
all the hard work and crazy hacks for you.
Really simple.
Features
--------
- Extremely simple GET, HEAD, POST, PUT, DELETE Requests
- Extremely simple HEAD, GET, POST, PUT, PATCH, DELETE Requests
+ Simple HTTP Header Request Attachment
+ Simple Data/Params Request Attachment
+ Simple Multipart File Uploads
+ CookieJar Support
+ Redirection History
+ Proxy Support
+ Redirection Recursion Urllib Fix
+ Auto Decompression of GZipped Content
+ Unicode URL Support
- Simple Authentication
+ Simple URL + HTTP Auth Registry
@@ -31,15 +60,14 @@ It couldn't be simpler. ::
HTTPS? Basic Authentication? ::
>>> r = requests.get('https://convore.com/api/account/verify.json')
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass')
>>> r.status_code
401
Uh oh, we're not authorized! Let's add authentication. ::
>>> conv_auth = ('requeststest', 'requeststest')
>>> r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass', auth=('user', 'pass'))
>>> r.status_code
200
@@ -48,74 +76,7 @@ Uh oh, we're not authorized! Let's add authentication. ::
'application/json'
>>> r.content
'{"username": "requeststest", "url": "/users/requeststest/", "id": "9408", "img": "censored-long-url"}'
API
---
**Requests:**
All request functions return a Response object (see below).
If a {filename: fileobject} dictionary is passed in (files=...), a multipart_encode upload will be performed.
If CookieJar object is is passed in (cookies=...), the cookies will be sent with the request.
GET Requests
>>> requests.get(url, params={}, headers={}, cookies=None, auth=None)
<Response [200]>
HEAD Requests
>>> requests.head(url, params={}, headers={}, cookies=None, auth=None)
<Response [200]>
PUT Requests
>>> requests.put(url, data='', headers={}, files={}, cookies=None, auth=None)
<Response [200]>
POST Requests
>>> requests.post(url, data={}, headers={}, files={}, cookies=None, auth=None)
<Response [200]>
DELETE Requests
>>> requests.delete(url, params={}, headers={}, cookies=None, auth=None)
<Response [200]>
**Responses:**
Response.status_code
(Integer) Received HTTP Status Code Response
Response.headers
(Dictionary) Received HTTP Response Headers
Response.content
(Bytes) Received Content
Response.url
(String) URL of response. Useful for detecting redirects.
Response.ok
(Bool) True if no errors occurred during the request, and the status_code is kosher.
Response.cached
(Bool) True if Response.content is stored within the object.
Response.error
(HTTPError) If an HTTPError occurred (e.g. status of 404), Otherwise this is None.
Response.raise_for_status()
Raises HTTPError if a request is not kosher.
**HTTP Authentication Registry:**
You can register AuthObjects to automatically enable HTTP Authentication on requests that contain a registered base URL string.
>>> requests.auth_manager.add_auth(url, authobject)
'{"authenticated": true, "user": "user"}'
Installation
@@ -139,14 +100,5 @@ Contribute
If you'd like to contribute, simply fork `the repository`_, commit your changes to the **develop** branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS_.
Roadmap
-------
- Sphinx Documentation (http://code.kennethreitz.com/requests/)
- Exhaustive unit tests
- Get rid of Poster (gets really nasty in py3.x)
- Python 3.x Support
.. _`the repository`: http://github.com/kennethreitz/requests
.. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS

5
debian/changelog vendored Normal file
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 = ''

93
docs/api.rst Normal file
View File

@@ -0,0 +1,93 @@
.. _api:
API
===
.. module:: requests
This part of the documentation covers all the interfaces of Requests. For
parts where Requests depends on external libraries, we document the most
important right here and provide links to the canonical documentation.
Main Interface
--------------
All of Request's functionality can be accessed by these 7 methods.
They all return an instance of the :class:`Response <Response>` object.
.. autofunction:: request
.. autofunction:: head
.. autofunction:: get
.. autofunction:: post
.. autofunction:: put
.. autofunction:: patch
.. autofunction:: delete
-----------
.. autoclass:: Response
:inherited-members:
Utilities
---------
These functions are used internally, but may be useful outside of
Requests.
.. module:: requests.utils
Cookies
~~~~~~~
.. autofunction:: dict_from_cookiejar
.. autofunction:: cookiejar_from_dict
.. autofunction:: add_dict_to_cookiejar
Curl
~~~~
.. autofunction:: curl_from_request
Encodings
~~~~~~~~~
.. autofunction:: get_encodings_from_content
.. autofunction:: get_encoding_from_headers
.. autofunction:: get_unicode_from_response
.. autofunction:: decode_gzip
Internals
---------
These items are an internal component to Requests, and should never be
seen by the end user (developer). This part of the API documentation
exists for those who are extending the functionality of Requests.
Exceptions
~~~~~~~~~~
.. module:: requests
.. autoexception:: HTTPError
.. autoexception:: RequestException
.. autoexception:: AuthenticationError
.. autoexception:: URLRequired
.. autoexception:: InvalidMethod
.. autoexception:: TooManyRedirects
Classes
~~~~~~~
.. autoclass:: requests.Request
:inherited-members:

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

@@ -0,0 +1,83 @@
.. _faq:
Frequently Asked Questions
==========================
This part of the documentation answers common questions about Requests.
Encoded Data?
-------------
Requests automatically decompresses gzip-encoded responses, and does
its best to decodes response content to unicode when possible.
You can get direct access to the raw response (and even the socket),
if needed as well.
Custom User-Agents?
-------------------
Requests allows you to easily override User-Agent strings, along with
any other HTTP Header.
Why not Httplib2?
-----------------
Chris Adams gave an excellent summary on
`Hacker News <http://news.ycombinator.com/item?id=2884406>`_:
httplib2 is part of why you should use requests: it's far more respectable
as a client but not as well documented and it still takes way too much code
for basic operations. I appreciate what httplib2 is trying to do, that
there's a ton of hard low-level annoyances in building a modern HTTP
client, but really, just use requests instead. Kenneth Reitz is very
motivated and he gets the degree to which simple things should be simple
whereas httplib2 feels more like an academic exercise than something
people should use to build production systems[1].
Disclosure: I'm listed in the requests AUTHORS file but can claim credit
for, oh, about 0.0001% of the awesomeness.
1. http://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
an annoying bug which affect many people, there was a fix available for
months, which worked great when I applied it in a fork and pounded a couple
TB of data through it, but it took over a year to make it into trunk and
even longer to make it onto PyPI where any other project which required "
httplib2" would get the working version.
Python 3 Support?
-----------------
It's on the way. Here's a list of Python platforms that are officially
supported:
* cPython 2.5
* cPython 2.5.5
* cPython 2.5.6
* cPython 2.6
* cPython 2.6.6
* cPython 2.6.7
* cPython 2.7
* cPython 2.7.1
* cPython 2.7.2
* PyPy-c 1.4
* PyPy-c 1.5
Support for Python 3.x is coming *very* soon.
Keep-alive Support?
-------------------
It's on the way.
Proxy Support?
--------------
You bet!

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,30 @@
.. _updates:
Updates
=======
If you'd like to stay up to date on the community and development of Requests,
there are serveral options:
GitHub
------
The best way to track the development of Requests is through
`the GitHub repo <https://github.com/kennethreitz/requests>`_.
Twitter
-------
I often tweet about new features and releases of Requests.
Follow `@kennethreitz <https://twitter.com/kennethreitz>`_ for updates.
Mailing List
------------
There's a low-volume mailing list for Requests. To subscribe to the
mailing list, send an email to
`requests@librelist.org <mailto:requests@librelist.org>`_.

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 -----------------------------------------------------
@@ -41,14 +43,14 @@ master_doc = 'index'
# General information about the project.
project = u'Requests'
copyright = u'2011, Kenneth Reitz'
copyright = u'2011. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.3.3'
version = __version__
# The full version, including alpha/beta/rc tags.
release = version
@@ -81,7 +83,7 @@ exclude_patterns = ['_build']
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = 'flask_theme_support.FlaskyStyle'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@@ -112,6 +114,7 @@ html_theme = 'default'
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
@@ -131,7 +134,12 @@ html_static_path = ['_static']
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
@@ -147,10 +155,10 @@ html_static_path = ['_static']
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
html_show_sphinx = False
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
@@ -230,3 +238,7 @@ texinfo_documents = [
# Documents to append as an appendix to all manuals.
texinfo_appendices = []
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'

5
docs/dev/authors.rst Normal file
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,114 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Requests's documentation!
====================================
Requests: HTTP for Humans
=========================
Contents:
Release v\ |version|. (:ref:`Installation <install>`)
Requests is an :ref:`ISC Licensed <isc>` HTTP library, written in Python, for human beings.
Most existing Python modules for sending HTTP requests are extremely verbose
and cumbersome. Python's builtin **urllib2** module provides most of
the HTTP capabilities you should need, but the api is thoroughly **broken**.
It requires an *enormous* amount of work (even method overrides) to perform
the simplest of tasks.
Things 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.
Testimonials
------------
`Twitter, Inc <http://twitter.com>`_,
a U.S. Federal Institution,
`Readability <http://readability.com>`_, and
`Work for Pie <http://workforpie.com>`_
use Requests internally.
**Armin Ronacher**
Requests is the perfect example how beautiful an API can be with the
right level of abstraction.
**Daniel Greenfeld**
Nuked a 1200 LOC spaghetti code library with 10 lines of code thanks to
@kennethreitz's request library. Today has been AWESOME.
**Kenny Meyers**
Python HTTP: When in doubt, or when not in doubt, use Requests. Beautiful,
simple, Pythonic.
**Rich Leland**
Requests is awesome. That is all.
User Guide
----------
This part of the documentation, which is mostly prose, begins with some
background information about Requests, then focuses on step-by-step
instructions for getting the most out of Requests.
.. toctree::
:maxdepth: 2
Indices and tables
==================
user/intro
user/install
user/quickstart
user/advanced
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Community Guide
-----------------
This part of the documentation, which is mostly prose, details the
Requests ecosystem and community.
.. toctree::
:maxdepth: 2
community/faq
community/support
community/updates
API Documentation
-----------------
If you are looking for information on a specific function, class or method,
this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
Developer Guide
---------------
If you want to contribute to the project, this part of the documentation is for
you.
.. toctree::
:maxdepth: 2
dev/internals
dev/todo
dev/authors

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

@@ -0,0 +1,171 @@
.. _advanced:
Advanced Usage
==============
This document covers some of Requests more advanced features.
Session Objects
---------------
The Session object allows you to persist certain parameters across
requests. It also establishes a CookieJar and passes it along
to any requests made from the Session instance.
A session object has all the methods of the main Requests API.
Let's persist some cookies across requests::
with requests.session() as s:
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print r.content
# '{"cookies": {"sessioncookie": "123456789"}}'
Sessions can also be used to provide default data to the request methods::
headers = {'x-test': 'true'}
auth = ('user', 'pass')
with requests.session(auth=auth, headers=headers) as c:
# both 'x-test' and 'x-test2' are sent
c.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
.. admonition:: Global Settings
Certain parameters are best set at the ``request.config`` level
(e.g.. a global proxy, user agent header).
Asynchronous Requests
----------------------
Requests has first-class support for non-blocking i/o requests, powered
by gevent. This allows you to send a bunch of HTTP requests at the same
First, let's import the async module. Heads up — if you don't have
**gevent** installed, this will fail.::
from requests import async
The ``async`` module has the exact same api as ``requests``, except it
doesn't send the request immediately. Instead, it returns the ``Request``
object.
We can build a list of ``Request`` objects easily::
urls = [
'http://python-requests.org',
'http://httpbin.org',
'http://python-guide.org',
'http://kennethreitz.com'
]
rs = [async.get(u) for u in urls]
Now we have a list of ``Request`` objects, ready to be sent. We could
send them one at a time with ``Request.send()``, but that would take a while.
Instead, we'll send them all at the same time with ``async.map()``::
>>> async.map(rs)
[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]
Event Hooks
-----------
Requests has a hook system that you can use to manipulate portions of
the request process, or signal event handling.
Available hooks:
``args``:
A dictionary of the arguments being sent to Request().
``pre_request``:
The Request object, directly before being sent.
``post_request``:
The Request object, directly after being sent.
``response``:
The response generated from a Request.
You can assign a hook function on a per-request basis by passing a
``{hook_name: callback_function}`` dictionary to the ``hooks`` request
parameter::
hooks=dict(args=print_url)
That ``callback_function`` will receive a chunk of data as its first
argument.
::
def print_url(args):
print args['url']
If an error occurs while executing your callback, a warning is given.
If the callback function returns a value, it is assumed that it is to
replace the data that was passed in. If the function doesn't return
anything, nothing else is effected.
Let's print some request method arguments at runtime::
>>> requests.get('http://httpbin', hooks=dict(args=print_url))
http://httpbin
<Response [200]>
Let's hijack some arguments this time with a new callback::
def hack_headers(args):
if not args[headers]:
args['headers'] = dict()
args['headers'].update({'X-Testing': 'True'})
return args
hooks = dict(args=hack_headers)
headers = dict(yo=dawg)
And give it a try::
>>> requests.get('http://httpbin/headers', hooks=hooks, headers=headers)
{
"headers": {
"Content-Length": "",
"Accept-Encoding": "gzip",
"Yo": "dawg",
"X-Forwarded-For": "::ffff:24.127.96.129",
"Connection": "close",
"User-Agent": "python-requests.org",
"Host": "httpbin.org",
"X-Testing": "True",
"X-Forwarded-Protocol": "",
"Content-Type": ""
}
}
Verbose Logging
---------------
If you want to get a good look at what HTTP requests are being sent
by your application, you can turn on verbose logging.
To do so, just configure Requests with a stream to write to::
>>> requests.settings.verbose = sys.stderr
>>> requests.get('http://httpbin.org/headers')
2011-08-17T03:04:23.380175 GET http://httpbin.org/headers
<Response [200]>

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

@@ -0,0 +1,56 @@
.. _install:
Installation
============
This part of the documentation covers the installation of Requests.
The first step to using any software package is getting it properly installed.
Distribute & Pip
----------------
Installing requests is simple with `pip <http://www.pip-installer.org/>`_::
$ pip install requests
or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
$ easy_install requests
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/index.html#pip-compared-to-easy-install>`_.
Cheeseshop Mirror
-----------------
If the Cheeseshop is down, you can also install Requests from Kenneth Reitz's
personal `Cheeseshop mirror <http://pip.kreitz.co/>`_::
$ pip install -i http://pip.kreitz.co/simple requests
Get the Code
------------
Requests is actively developed on GitHub, where the code is
`always available <https://github.com/kennethreitz/requests>`_.
You can either clone the public repository::
git clone git://github.com/kennethreitz/requests.git
Download the `tarball <https://github.com/kennethreitz/requests/tarball/master>`_::
$ curl -O https://github.com/kennethreitz/requests/tarball/master
Or, download the `zipball <https://github.com/kennethreitz/requests/zipball/master>`_::
$ curl -O https://github.com/kennethreitz/requests/zipball/master
Once you have a copy of the source, you can embed it in your Python package,
or install it into your site-packages easily::
$ python setup.py install

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

@@ -0,0 +1,51 @@
.. _introduction:
Introduction
============
Philosophy
----------
Requests was developed with a few :pep:`20` idioms in mind.
#. Beautiful is better than ugly.
#. Explicit is better than implicit.
#. Simple is better than complex.
#. Complex is better than complicated.
#. Readability counts.
All contributions to Requests should keep these important rules in mind.
ISC License
-----------
A large number of open source projects you find today are `GPL Licensed`_.
While the GPL has its time and place, it should most certainly not be your
go-to license for your next open source project.
A project that is released as GPL cannot be used in any commercial product
without the product itself also being offered as open source.
The MIT, BSD, ISC, and Apache2 licenses are great alternatives to the GPL
that allow your open-source software to be used freely in proprietary,
closed-source software.
Requests is released under terms of `The ISC License`_.
.. _`GPL Licensed`: http://www.opensource.org/licenses/gpl-license.php
.. _`The ISC License`: http://www.opensource.org/licenses/isc-license
Requests License
----------------
Copyright (c) 2011, Kenneth Reitz
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

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

@@ -0,0 +1,138 @@
.. _quickstart:
Quickstart
==========
.. module:: requests.models
Eager to get started? This page gives a good introduction in how to get started
with Requests. This assumes you already have Requests installed. If you do not,
head over to the :ref:`Installation <install>` section.
First, make sure that:
* Requests is :ref:`installed <install>`
* Requests is :ref:`up-to-date <updates>`
Lets gets started with some simple use cases and examples.
Make a GET Request
------------------
Making a standard request with Requests is very simple.
Let's get GitHub's public timeline ::
r = requests.get('https://github.com/timeline.json')
Now, we have a :class:`Response` object called ``r``. We can get all the
information we need from this.
Response Content
----------------
We can read the content of the server's response::
>>> r.content
'[{"repository":{"open_issues":0,"url":"https://github.com/...
Response Status Codes
---------------------
We can check the response status code::
>>> r.status_code
200
Requests also comes with a built-in status code lookup object for easy
reference::
>>> r.status_code == requests.codes.ok
True
If we made a bad request, we can raise it with
:class:`Response.raise_for_status()`::
>>> _r = requests.get('http://httpbin.org/status/404')
>>> _r.status_code
404
>>> _r.raise_for_status()
Traceback (most recent call last):
File "requests/models.py", line 394, in raise_for_status
raise self.error
urllib2.HTTPError: HTTP Error 404: NOT FOUND
But, since our ``status_code`` was ``200``, when we call it::
>>> r.raise_for_status()
None
All is well.
Response Headers
----------------
We can view the server's response headers with a simple Python dictionary
interface::
>>> r.headers
{
'status': '200 OK',
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json; charset=utf-8'
}
The dictionary is special, though: it's made just for HTTP headers. According to
`RFC 2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>`_, HTTP
Headers are case-insensitive.
So, we can access the headers using any capitalization we want::
>>> r.headers['Content-Type']
'application/json; charset=utf-8'
>>> r.headers.get('content-type')
'application/json; charset=utf-8'
If a header doesn't exist in the Response, its value defaults to ``None``::
>>> r.headers['X-Random']
None
Cookies
-------
If a response contains some Cookies, you can get quick access to them::
>>> url = 'http://httpbin.org/cookies/set/requests-is/awesome'
>>> r = requests.get(url)
>>> print r.cookies
{'requests-is': 'awesome'}
The underlying CookieJar is also available for more advanced handling::
>>> r.request.cookiejar
<cookielib.CookieJar>
To send your own cookies to the server, you can use the ``cookies``
parameter::
>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.content
'{"cookies": {"cookies_are": "working"}}'

2217
ext/requests-logo.ai Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +1,4 @@
# -*- coding: utf-8 -*-
import inspect
import packages
from core import *
from core import __version__
timeout = None
class settings:
"""Context manager for settings."""
cache = {}
def __init__(self, timeout):
self.module = inspect.getmodule(self)
# Cache settings
self.cache['timeout'] = self.module.timeout
self.module.timeout = timeout
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
# Restore settings
for key in self.cache:
setattr(self.module, key, self.cache[key])

View File

@@ -11,105 +11,182 @@ This module impliments the Requests API.
"""
import requests
from .models import Request, Response, AuthManager, AuthObject, auth_manager
import config
from .models import Request, Response, AuthObject
from .status_codes import codes
from .hooks import dispatch_hook
from .utils import cookiejar_from_dict
__all__ = ('request', 'get', 'head', 'post', 'put', 'delete')
__all__ = ('request', 'get', 'head', 'post', 'patch', 'put', 'delete')
def request(method, url,
params=None, data=None, headers=None, cookies=None, files=None, auth=None,
timeout=None, allow_redirects=False, proxies=None, hooks=None, return_response=True):
def request(method, url, **kwargs):
"""Sends a `method` request. Returns :class:`Response` object.
"""Constructs and sends a :class:`Request <models.Request>`.
Returns :class:`Response <models.Response>` object.
:param method: method for the new :class:`Request` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET/HEAD/DELETE Parameters to send with the :class:`Request`.
:param data: (optional) Bytes/Dictionary of PUT/POST Data to send with the :class:`Request`.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
data = kwargs.pop('data', dict()) or kwargs.pop('params', dict())
r = Request(method=method, url=url, data=data, headers=kwargs.pop('headers', {}),
cookiejar=kwargs.pop('cookies', None), files=kwargs.pop('files', None),
auth=kwargs.pop('auth', auth_manager.get_auth(url)),
timeout=kwargs.pop('timeout', requests.timeout))
if cookies is None:
cookies = {}
cookies = cookiejar_from_dict(cookies)
args = dict(
method = method,
url = url,
data = data,
params = params,
headers = headers,
cookiejar = cookies,
files = files,
auth = auth,
hooks = hooks,
timeout = timeout or config.settings.timeout,
allow_redirects = allow_redirects,
proxies = proxies or config.settings.proxies,
)
# Arguments manipulation hook.
args = dispatch_hook('args', hooks, args)
r = Request(**args)
# Pre-request hook.
r = dispatch_hook('pre_request', hooks, r)
# Don't send if asked nicely.
if not return_response:
return r
# Send the HTTP Request.
r.send()
# Post-request hook.
r = dispatch_hook('post_request', hooks, r)
# Response manipulation hook.
r.response = dispatch_hook('response', hooks, r.response)
return r.response
def get(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def get(url, **kwargs):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to False to disable redirect following.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('GET', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
kwargs.setdefault('allow_redirects', True)
return request('GET', url, **kwargs)
def head(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def head(url, **kwargs):
"""Sends a HEAD request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to False to disable redirect following.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('HEAD', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
kwargs.setdefault('allow_redirects', True)
return request('HEAD', url, **kwargs)
def post(url, data={}, headers={}, files=None, cookies=None, auth=None, **kwargs):
def post(url, data='', **kwargs):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary of POST data to send with the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('POST', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
return request('POST', url, data=data, **kwargs)
def put(url, data='', headers={}, files={}, cookies=None, auth=None, **kwargs):
def put(url, data='', **kwargs):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Bytes of PUT Data to send with the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('PUT', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
return request('PUT', url, data=data, **kwargs)
def delete(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def patch(url, data='', **kwargs):
"""Sends a PATCH request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('PATCH', url, **kwargs)
def delete(url, **kwargs):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of DELETE Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if redirect following is allowed.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('DELETE', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
return request('DELETE', url, **kwargs)

View File

@@ -1,48 +1,84 @@
# -*- coding: utf-8 -*-
"""
requests.async
~~~~~~~~~~~~~~
requests.async
~~~~~~~~~~~~~~
This module implements the main Requests system, after monkey-patching
the urllib2 module with eventlet or gevent..
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
This module contains an asynchronous replica of ``requests.api``, powered
by gevent. All API methods return a ``Request`` instance (as opposed to
``Response``). A list of requests can be sent with ``map()``.
"""
from __future__ import absolute_import
import urllib
import urllib2
from urllib2 import HTTPError
try:
import eventlet
eventlet.monkey_patch()
import gevent
from gevent import monkey as curious_george
except ImportError:
pass
raise RuntimeError('Gevent is required for requests.async.')
if not 'eventlet' in locals():
try:
from gevent import monkey
monkey.patch_all()
except ImportError:
pass
# Monkey-patch.
curious_george.patch_all(thread=False)
from . import api
from .hooks import dispatch_hook
if not 'eventlet' in locals():
raise ImportError('No Async adaptations of urllib2 found!')
__all__ = (
'map',
'get', 'head', 'post', 'put', 'patch', 'delete', 'request'
)
def _patched(f):
"""Patches a given API function to not send."""
def wrapped(*args, **kwargs):
return f(*args, return_response=False, **kwargs)
return wrapped
def _send(r, pools=None):
"""Sends a given Request object."""
if pools:
r._pools = pools
r.send()
# Post-request hook.
r = dispatch_hook('post_request', r.hooks, r)
# Response manipulation hook.
r.response = dispatch_hook('response', r.hooks, r.response)
return r.response
# Patched requests.api functions.
get = _patched(api.get)
head = _patched(api.head)
post = _patched(api.post)
put = _patched(api.put)
patch = _patched(api.patch)
delete = _patched(api.delete)
request = _patched(api.request)
def map(requests, prefetch=True):
"""Concurrently converts a list of Requests to Responses.
:param requests: a collection of Request objects.
:param prefetch: If False, the content will not be downloaded immediately.
"""
jobs = [gevent.spawn(_send, r) for r in requests]
gevent.joinall(jobs)
if prefetch:
[r.response.content for r in requests]
return [r.response for r in requests]
from .core import *
__all__ = [
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
'URLRequired', 'InvalidMethod', 'HTTPError'
]

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

@@ -12,12 +12,16 @@ This module implements the main Requests system.
"""
__title__ = 'requests'
__version__ = '0.3.4'
__build__ = 0x000304
__version__ = '0.6.3'
__build__ = 0x000603
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
from .models import HTTPError, auth_manager
from .api import *
from models import HTTPError
from api import *
from exceptions import *
from sessions import session
from status_codes import codes
from config import settings

26
requests/exceptions.py Normal file
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

View File

@@ -1,83 +1,123 @@
# -*- coding: utf-8 -*-
"""
requests.system
requests.models
~~~~~~~~~~~~~~~
"""
import requests
import urllib
import urllib2
import socket
import zlib
from urllib2 import HTTPError
from urlparse import urlparse
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPDigestAuthHandler
from urlparse import urlparse, urlunparse, urljoin
from datetime import datetime
from .config import settings
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPForcedBasicAuthHandler, HTTPDigestAuthHandler, HTTPRedirectHandler
from .structures import CaseInsensitiveDict
from .packages.poster.encode import multipart_encode
from .packages.poster.streaminghttp import register_openers, get_handlers
from .utils import dict_from_cookiejar
from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects
from .status_codes import codes
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
class Request(object):
"""The :class:`Request` object. It carries out all functionality of
"""The :class:`Request <models.Request>` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
def __init__(self,
url=None, headers=dict(), files=None, method=None, data=dict(),
params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False,
allow_redirects=False, proxies=None, hooks=None):
def __init__(self, url=None, headers=dict(), files=None, method=None,
data=dict(), auth=None, cookiejar=None, timeout=None):
socket.setdefaulttimeout(timeout)
#: Float describ the timeout of the request.
# (Use socket.setdefaulttimeout() as fallback)
self.timeout = timeout
#: Request URL.
self.url = url
#: Dictonary of HTTP Headers to attach to the :class:`Request <models.Request>`.
self.headers = headers
#: Dictionary of files to multipart upload (``{filename: content}``).
self.files = files
#: HTTP Method to use. Available: GET, HEAD, PUT, POST, DELETE.
self.method = method
self.data = {}
# self.data = {}
if hasattr(data, 'items'):
for (k, v) in data.items():
self.data.update({
k.encode('utf-8') if isinstance(k, unicode) else k:
v.encode('utf-8') if isinstance(v, unicode) else v
})
#: Dictionary or byte of request body data to attach to the
#: :class:`Request <models.Request>`.
self.data = None
# url encode data if it's a dict
if hasattr(data, 'items'):
self._enc_data = urllib.urlencode(self.data)
else:
self._enc_data = data
#: Dictionary or byte of querystring data to attach to the
#: :class:`Request <models.Request>`.
self.params = None
#: True if :class:`Request <models.Request>` is part of a redirect chain (disables history
#: and HTTPError storage).
self.redirect = redirect
#: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
self.allow_redirects = allow_redirects
# Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
self.proxies = proxies
self.data, self._enc_data = self._encode_params(data)
self.params, self._enc_params = self._encode_params(params)
#: :class:`Response <models.Response>` instance, containing
#: content and metadata of HTTP Response, once :attr:`sent <send>`.
self.response = Response()
if isinstance(auth, (list, tuple)):
auth = AuthObject(*auth)
if not auth:
auth = auth_manager.get_auth(self.url)
#: :class:`AuthObject` to attach to :class:`Request <models.Request>`.
self.auth = auth
#: CookieJar to attach to :class:`Request <models.Request>`.
self.cookiejar = cookiejar
#: True if Request has been sent.
self.sent = False
#: Event-handling hooks.
self.hooks = hooks
# Header manipulation and defaults.
if settings.accept_gzip:
settings.base_headers.update({'Accept-Encoding': 'gzip'})
if headers:
headers = CaseInsensitiveDict(self.headers)
else:
headers = CaseInsensitiveDict()
for (k, v) in settings.base_headers.items():
if k not in headers:
headers[k] = v
self.headers = headers
def __repr__(self):
return '<Request [%s]>' % (self.method)
def __setattr__(self, name, value):
if (name == 'method') and (value):
if not value in self._METHODS:
raise InvalidMethod()
object.__setattr__(self, name, value)
def _checks(self):
"""Deterministic checks for consistency."""
@@ -95,16 +135,24 @@ class Request(object):
if self.auth:
if not isinstance(self.auth.handler, (urllib2.AbstractBasicAuthHandler, urllib2.AbstractDigestAuthHandler)):
# TODO: REMOVE THIS COMPLETELY
auth_manager.add_password(self.auth.realm, self.url, self.auth.username, self.auth.password)
self.auth.handler = self.auth.handler(auth_manager)
auth_manager.add_auth(self.url, self.auth)
_handlers.append(self.auth.handler)
if self.proxies:
_handlers.append(urllib2.ProxyHandler(self.proxies))
_handlers.append(HTTPRedirectHandler)
if not _handlers:
return urllib2.urlopen
_handlers.extend(get_handlers())
if self.data or self.files:
_handlers.extend(get_handlers())
opener = urllib2.build_opener(*_handlers)
if self.headers:
@@ -118,40 +166,128 @@ class Request(object):
return opener.open
def _build_response(self, resp):
"""Build internal Response object from given response."""
if isinstance(resp, HTTPError):
# print resp.__dict__
pass
self.response.status_code = getattr(resp, 'code', None)
def _build_response(self, resp, is_error=False):
"""Build internal :class:`Response <models.Response>` object from given response."""
try:
self.response.headers = getattr(resp.info(), 'dict', None)
self.response.content = resp.read()
except AttributeError, why:
pass
def build(resp):
response = Response()
response.status_code = getattr(resp, 'code', None)
if self.response.headers.get('content-encoding', None) == 'gzip':
try:
self.response.content = zlib.decompress(self.response.content, 16+zlib.MAX_WBITS)
except zlib.error:
response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None))
response.read = resp.read
response._resp = resp
response._close = resp.close
if self.cookiejar:
response.cookies = dict_from_cookiejar(self.cookiejar)
except AttributeError:
pass
self.response.url = getattr(resp, 'url', None)
if is_error:
response.error = resp
response.url = getattr(resp, 'url', None)
return response
history = []
r = build(resp)
if r.status_code in REDIRECT_STATI and not self.redirect:
while (
('location' in r.headers) and
((r.status_code is codes.see_other) or (self.allow_redirects))
):
r.close()
if not len(history) < settings.max_redirects:
raise TooManyRedirects()
history.append(r)
url = r.headers['location']
# Handle redirection without scheme (see: RFC 1808 Section 4)
if url.startswith('//'):
parsed_rurl = urlparse(r.url)
url = '%s:%s' % (parsed_rurl.scheme, url)
# Facilitate non-RFC2616-compliant 'location' headers
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
if not urlparse(url).netloc:
url = urljoin(r.url, urllib.quote(urllib.unquote(url)))
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
if r.status_code is codes.see_other:
method = 'GET'
else:
method = self.method
request = Request(
url, self.headers, self.files, method,
self.data, self.params, self.auth, self.cookiejar,
redirect=True
)
request.send()
r = request.response
r.history = history
self.response = r
self.response.request = self
@staticmethod
def _build_url(url, data=None):
"""Build URLs."""
def _encode_params(data):
"""Encode parameters in a piece of data.
if urlparse(url).query:
return '%s&%s' % (url, data)
If the data supplied is a dictionary, encodes each parameter in it, and
returns a list of tuples containing the encoded parameters, and a urlencoded
version of that.
Otherwise, assumes the data is already encoded appropriately, and
returns it twice.
"""
if hasattr(data, 'items'):
result = []
for k, vs in data.items():
for v in isinstance(vs, list) and vs or [vs]:
result.append((k.encode('utf-8') if isinstance(k, unicode) else k,
v.encode('utf-8') if isinstance(v, unicode) else v))
return result, urllib.urlencode(result, doseq=True)
else:
if data:
return '%s?%s' % (url, data)
return data, data
def _build_url(self):
"""Build the actual URL to use."""
# Support for unicode domain names and paths.
scheme, netloc, path, params, query, fragment = urlparse(self.url)
netloc = netloc.encode('idna')
if isinstance(path, unicode):
path = path.encode('utf-8')
path = urllib.quote(urllib.unquote(path))
self.url = str(urlunparse([ scheme, netloc, path, params, query, fragment ]))
if self._enc_params:
if urlparse(self.url).query:
return '%s&%s' % (self.url, self._enc_params)
else:
return url
return '%s?%s' % (self.url, self._enc_params)
else:
return self.url
def send(self, anyway=False):
@@ -164,11 +300,20 @@ class Request(object):
:param anyway: If True, request will be sent, even if it has
already been sent.
"""
self._checks()
success = False
# Logging
if settings.verbose:
settings.verbose.write('%s %s %s\n' % (
datetime.now().isoformat(), self.method, self.url
))
url = self._build_url()
if self.method in ('GET', 'HEAD', 'DELETE'):
req = _Request(self._build_url(self.url, self._enc_data), method=self.method)
req = _Request(url, method=self.method)
else:
if self.files:
@@ -178,58 +323,95 @@ class Request(object):
self.files.update(self.data)
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method=self.method)
req = _Request(url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(self.url, data=self._enc_data, method=self.method)
req = _Request(url, data=self._enc_data, method=self.method)
if self.headers:
req.headers.update(self.headers)
for k,v in self.headers.iteritems():
req.add_header(k, v)
if not self.sent or anyway:
try:
opener = self._get_opener()
resp = opener(req)
try:
resp = opener(req, timeout=self.timeout)
except TypeError, err:
# timeout argument is new since Python v2.6
if not 'timeout' in str(err):
raise
if settings.timeout_fallback:
# fall-back and use global socket timeout (This is not thread-safe!)
old_timeout = socket.getdefaulttimeout()
socket.setdefaulttimeout(self.timeout)
resp = opener(req)
if settings.timeout_fallback:
# restore gobal timeout
socket.setdefaulttimeout(old_timeout)
if self.cookiejar is not None:
self.cookiejar.extract_cookies(resp, req)
except urllib2.HTTPError, why:
self._build_response(why)
self.response.error = why
except (urllib2.HTTPError, urllib2.URLError), why:
if hasattr(why, 'reason'):
if isinstance(why.reason, socket.timeout):
why = Timeout(why)
elif isinstance(why.reason, socket.error):
why = Timeout(why)
self._build_response(why, is_error=True)
else:
self._build_response(resp)
self.response.ok = True
self.response.cached = False
else:
self.response.cached = True
self.sent = self.response.ok
return self.sent
def read(self, *args):
return self.response.read()
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
:class:`Request.response <response>` attribute, which is an instance of
this class.
"""The core :class:`Response <models.Response>` object. All
:class:`Request <models.Request>` objects contain a
:class:`response <models.Response>` attribute, which is an instance
of this class.
"""
def __init__(self):
self.content = None
#: Raw content of the response, in bytes.
#: If ``content-encoding`` of response was set to ``gzip``, the
#: response data will be automatically deflated.
self._content = None
#: Integer Code of responded HTTP Status.
self.status_code = None
self.headers = dict()
#: Case-insensitive Dictionary of Response Headers.
#: For example, ``headers['content-encoding']`` will return the
#: value of a ``'Content-Encoding'`` response header.
self.headers = CaseInsensitiveDict()
#: Final URL location of Response.
self.url = None
#: True if no :attr:`error` occured.
self.ok = False
#: Resulting :class:`HTTPError` of request, if one occured.
self.error = None
self.cached = False
#: A list of :class:`Response <models.Response>` objects from
#: the history of the Request. Any redirect responses will end
#: up here.
self.history = []
#: The Request that created the Response.
self.request = None
#: A dictionary of Cookies the server sent back.
self.cookies = None
def __repr__(self):
@@ -237,22 +419,38 @@ class Response(object):
def __nonzero__(self):
"""Returns true if status_code is 'OK'."""
"""Returns true if :attr:`status_code` is 'OK'."""
return not self.error
def __getattr__(self, name):
"""Read and returns the full stream when accessing to :attr: `content`"""
if name == 'content':
if self._content is not None:
return self._content
self._content = self.read()
if self.headers.get('content-encoding', '') == 'gzip':
try:
self._content = zlib.decompress(self._content, 16+zlib.MAX_WBITS)
except zlib.error:
pass
return self._content
else:
raise AttributeError
def raise_for_status(self):
"""Raises stored HTTPError if one exists."""
"""Raises stored :class:`HTTPError` or :class:`URLError`, if one occured."""
if self.error:
raise self.error
def read(self, *args):
return self.content
def close(self):
if self._resp.fp is not None and hasattr(self._resp.fp, '_sock'):
self._resp.fp._sock.recv = None
self._close()
class AuthManager(object):
"""Authentication Manager."""
"""Requests Authentication Manager."""
def __new__(cls):
singleton = cls.__dict__.get('__singleton__')
@@ -324,8 +522,10 @@ class AuthManager(object):
def reduce_uri(self, uri, default_port=True):
"""Accept authority or URI and extract only the authority and path."""
# note HTTP URLs do not have a userinfo component
parts = urllib2.urlparse.urlsplit(uri)
if parts[1]:
# URI
scheme = parts[0]
@@ -336,7 +536,9 @@ class AuthManager(object):
scheme = None
authority = uri
path = '/'
host, port = urllib2.splitport(authority)
if default_port and port is None and scheme is not None:
dport = {"http": 80,
"https": 443,
@@ -406,30 +608,18 @@ class AuthObject(object):
_handlers = {
'basic': HTTPBasicAuthHandler,
'forced_basic': HTTPForcedBasicAuthHandler,
'digest': HTTPDigestAuthHandler,
'proxy_basic': urllib2.ProxyBasicAuthHandler,
'proxy_digest': urllib2.ProxyDigestAuthHandler
}
def __init__(self, username, password, handler='basic', realm=None):
def __init__(self, username, password, handler='forced_basic', realm=None):
self.username = username
self.password = password
self.realm = realm
if isinstance(handler, basestring):
self.handler = self._handlers.get(handler.lower(), urllib2.HTTPBasicAuthHandler)
self.handler = self._handlers.get(handler.lower(), HTTPForcedBasicAuthHandler)
else:
self.handler = handler
class RequestException(Exception):
"""There was an ambiguous exception that occured while handling your
request."""
class AuthenticationError(RequestException):
"""The authentication credentials provided were invalid."""
class URLRequired(RequestException):
"""A valid URL is required to make a request."""
class InvalidMethod(RequestException):
"""An inappropriate method was attempted."""

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
#-*- coding: utf-8 -*-
"""
requests.monkeys
@@ -9,7 +9,7 @@ Urllib2 Monkey patches.
"""
import urllib2
import re
class Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
@@ -27,12 +27,23 @@ class Request(urllib2.Request):
return urllib2.Request.get_method(self)
class HTTPRedirectHandler(urllib2.HTTPRedirectHandler):
"""HTTP Redirect handler."""
def http_error_301(self, req, fp, code, msg, headers):
pass
http_error_302 = http_error_303 = http_error_307 = http_error_301
class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
# from mercurial
"""HTTP Basic Auth Handler with authentication loop fixes."""
def __init__(self, *args, **kwargs):
urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
self.retried_req = None
self.retried = 0
def reset_retry_count(self):
# Python 2.6.5 will call this on 401 or 407 errors and thus loop
@@ -40,13 +51,69 @@ class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
# http_error_auth_reqed instead.
pass
def http_error_auth_reqed(self, auth_header, host, req, headers):
# Reset the retry counter once for each request.
if req is not self.retried_req:
self.retried_req = req
self.retried = 0
return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
self, auth_header, host, req, headers
)
class HTTPForcedBasicAuthHandler(HTTPBasicAuthHandler):
"""HTTP Basic Auth Handler with forced Authentication."""
auth_header = 'Authorization'
rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
'realm=(["\'])(.*?)\\2', re.I)
def __init__(self, *args, **kwargs):
HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
def http_error_401(self, req, fp, code, msg, headers):
url = req.get_full_url()
response = self._http_error_auth_reqed('www-authenticate', url, req, headers)
self.reset_retry_count()
return response
http_error_404 = http_error_401
def _http_error_auth_reqed(self, authreq, host, req, headers):
authreq = headers.get(authreq, None)
if self.retried > 5:
# retry sending the username:password 5 times before failing.
raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed",
headers, None)
else:
self.retried += 1
if authreq:
mo = self.rx.search(authreq)
if mo:
scheme, quote, realm = mo.groups()
if scheme.lower() == 'basic':
response = self.retry_http_basic_auth(host, req, realm)
if response and response.code not in (401, 404):
self.retried = 0
return response
else:
response = self.retry_http_basic_auth(host, req, 'Realm')
if response and response.code not in (401, 404):
self.retried = 0
return response

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

View File

@@ -1,360 +1,65 @@
# -*- coding: utf-8 -*-
# from: werkzeug
"""
requests.structures
~~~~~~~~~~~~~~~~~~~
class TypeConversionDict(dict):
"""Works like a regular dict but the :meth:`get` method can perform
type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
are subclasses of this class and provide the same feature.
"""
Datastructures that power Requests.
def get(self, key, default=None, type=None):
"""Return the default value if the requested data doesn't exist.
If `type` is provided and is a callable it should convert the value,
return it or raise a :exc:`ValueError` if that is not possible. In
this case the function will return the default as if the value was not
found:
"""
>>> d = TypeConversionDict(foo='42', bar='blub')
>>> d.get('foo', type=int)
42
>>> d.get('bar', -1, type=int)
-1
class CaseInsensitiveDict(dict):
"""Case-insensitive Dictionary
:param key: The key to be looked up.
:param default: The default value to be returned if the key can't
be looked up. If not further specified `None` is
returned.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the default value is returned.
"""
try:
rv = self[key]
if type is not None:
rv = type(rv)
except (KeyError, ValueError):
rv = default
return rv
For example, ``headers['content-encoding']`` will return the
value of a ``'Content-Encoding'`` response header."""
@property
def lower_keys(self):
if not hasattr(self, '_lower_keys') or not self._lower_keys:
self._lower_keys = dict((k.lower(), k) for k in self.iterkeys())
return self._lower_keys
# from: werkzeug
class MultiDict(TypeConversionDict):
"""A :class:`MultiDict` is a dictionary subclass customized to deal with
multiple values for the same key which is for example used by the parsing
functions in the wrappers. This is necessary because some HTML form
elements pass multiple values for the same key.
:class:`MultiDict` implements all standard dictionary methods.
Internally, it saves all values for a key as a list, but the standard dict
access methods will only return the first value for a key. If you want to
gain access to the other values, too, you have to use the `list` methods as
explained below.
Basic Usage:
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
>>> d
MultiDict([('a', 'b'), ('a', 'c')])
>>> d['a']
'b'
>>> d.getlist('a')
['b', 'c']
>>> 'a' in d
True
It behaves like a normal dict thus all dict functions will only return the
first value when multiple values for one key are found.
From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
exceptions.
A :class:`MultiDict` can be constructed from an iterable of
``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
onwards some keyword parameters.
:param mapping: the initial value for the :class:`MultiDict`. Either a
regular dict, an iterable of ``(key, value)`` tuples
or `None`.
"""
# the key error this class raises. Because of circular dependencies
# with the http exception module this class is created at the end of
# this module.
KeyError = None
def __init__(self, mapping=None):
if isinstance(mapping, MultiDict):
dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
elif isinstance(mapping, dict):
tmp = {}
for key, value in mapping.iteritems():
if isinstance(value, (tuple, list)):
value = list(value)
else:
value = [value]
tmp[key] = value
dict.__init__(self, tmp)
else:
tmp = {}
for key, value in mapping or ():
tmp.setdefault(key, []).append(value)
dict.__init__(self, tmp)
def __getstate__(self):
return dict(self.lists())
def __setstate__(self, value):
dict.clear(self)
dict.update(self, value)
def __iter__(self):
return self.iterkeys()
def __getitem__(self, key):
"""Return the first data value for this key;
raises KeyError if not found.
:param key: The key to be looked up.
:raise KeyError: if the key does not exist.
"""
if key in self:
return dict.__getitem__(self, key)[0]
raise self.KeyError(key)
def _clear_lower_keys(self):
if hasattr(self, '_lower_keys'):
self._lower_keys.clear()
def __setitem__(self, key, value):
"""Like :meth:`add` but removes an existing key first.
dict.__setitem__(self, key, value)
self._clear_lower_keys()
:param key: the key for the value.
:param value: the value to set.
"""
dict.__setitem__(self, key, [value])
def __delitem__(self, key):
dict.__delitem__(self, key)
self._lower_keys.clear()
def add(self, key, value):
"""Adds a new value for the key.
def __contains__(self, key):
return key.lower() in self.lower_keys
.. versionadded:: 0.6
def __getitem__(self, key):
# We allow fall-through here, so values default to None
if key in self:
return dict.__getitem__(self, self.lower_keys[key.lower()])
:param key: the key for the value.
:param value: the value to add.
"""
dict.setdefault(self, key, []).append(value)
def getlist(self, key, type=None):
"""Return the list of items for a given key. If that key is not in the
`MultiDict`, the return value will be an empty list. Just as `get`
`getlist` accepts a `type` parameter. All items will be converted
with the callable defined there.
:param key: The key to be looked up.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the value will be removed from the list.
:return: a :class:`list` of all the values for the key.
"""
try:
rv = dict.__getitem__(self, key)
except KeyError:
return []
if type is None:
return list(rv)
result = []
for item in rv:
try:
result.append(type(item))
except ValueError:
pass
return result
def setlist(self, key, new_list):
"""Remove the old values for a key and add new ones. Note that the list
you pass the values in will be shallow-copied before it is inserted in
the dictionary.
>>> d = MultiDict()
>>> d.setlist('foo', ['1', '2'])
>>> d['foo']
'1'
>>> d.getlist('foo')
['1', '2']
:param key: The key for which the values are set.
:param new_list: An iterable with the new values for the key. Old values
are removed first.
"""
dict.__setitem__(self, key, list(new_list))
def setdefault(self, key, default=None):
"""Returns the value for the key if it is in the dict, otherwise it
returns `default` and sets that value for `key`.
:param key: The key to be looked up.
:param default: The default value to be returned if the key is not
in the dict. If not further specified it's `None`.
"""
if key not in self:
self[key] = default
def get(self, key, default=None):
if key in self:
return self[key]
else:
default = self[key]
return default
return default
def setlistdefault(self, key, default_list=None):
"""Like `setdefault` but sets multiple values. The list returned
is not a copy, but the list that is actually used internally. This
means that you can put new values into the dict by appending items
to the list:
class LookupDict(dict):
"""Dictionary lookup object."""
>>> d = MultiDict({"foo": 1})
>>> d.setlistdefault("foo").extend([2, 3])
>>> d.getlist("foo")
[1, 2, 3]
:param key: The key to be looked up.
:param default: An iterable of default values. It is either copied
(in case it was a list) or converted into a list
before returned.
:return: a :class:`list`
"""
if key not in self:
default_list = list(default_list or ())
dict.__setitem__(self, key, default_list)
else:
default_list = dict.__getitem__(self, key)
return default_list
def items(self, multi=False):
"""Return a list of ``(key, value)`` pairs.
:param multi: If set to `True` the list returned will have a
pair for each value of each key. Otherwise it
will only contain pairs for the first value of
each key.
:return: a :class:`list`
"""
return list(self.iteritems(multi))
def lists(self):
"""Return a list of ``(key, values)`` pairs, where values is the list of
all values associated with the key.
:return: a :class:`list`
"""
return list(self.iterlists())
def values(self):
"""Returns a list of the first value on every key's value list.
:return: a :class:`list`.
"""
return [self[key] for key in self.iterkeys()]
def listvalues(self):
"""Return a list of all values associated with a key. Zipping
:meth:`keys` and this is the same as calling :meth:`lists`:
>>> d = MultiDict({"foo": [1, 2, 3]})
>>> zip(d.keys(), d.listvalues()) == d.lists()
True
:return: a :class:`list`
"""
return list(self.iterlistvalues())
def iteritems(self, multi=False):
"""Like :meth:`items` but returns an iterator."""
for key, values in dict.iteritems(self):
if multi:
for value in values:
yield key, value
else:
yield key, values[0]
def iterlists(self):
"""Like :meth:`items` but returns an iterator."""
for key, values in dict.iteritems(self):
yield key, list(values)
def itervalues(self):
"""Like :meth:`values` but returns an iterator."""
for values in dict.itervalues(self):
yield values[0]
def iterlistvalues(self):
"""Like :meth:`listvalues` but returns an iterator."""
return dict.itervalues(self)
def copy(self):
"""Return a shallow copy of this object."""
return self.__class__(self)
def to_dict(self, flat=True):
"""Return the contents as regular dict. If `flat` is `True` the
returned dict will only have the first item present, if `flat` is
`False` all values will be returned as lists.
:param flat: If set to `False` the dict returned will have lists
with all the values in it. Otherwise it will only
contain the first value for each key.
:return: a :class:`dict`
"""
if flat:
return dict(self.iteritems())
return dict(self.lists())
def update(self, other_dict):
"""update() extends rather than replaces existing key lists."""
for key, value in iter_multi_items(other_dict):
MultiDict.add(self, key, value)
def pop(self, key, default=_missing):
"""Pop the first item for a list on the dict. Afterwards the
key is removed from the dict, so additional values are discarded:
>>> d = MultiDict({"foo": [1, 2, 3]})
>>> d.pop("foo")
1
>>> "foo" in d
False
:param key: the key to pop.
:param default: if provided the value to return if the key was
not in the dictionary.
"""
try:
return dict.pop(self, key)[0]
except KeyError, e:
if default is not _missing:
return default
raise self.KeyError(str(e))
def popitem(self):
"""Pop an item from the dict."""
try:
item = dict.popitem(self)
return (item[0], item[1][0])
except KeyError, e:
raise self.KeyError(str(e))
def poplist(self, key):
"""Pop the list for a key from the dict. If the key is not in the dict
an empty list is returned.
.. versionchanged:: 0.5
If the key does no longer exist a list is returned instead of
raising an error.
"""
return dict.pop(self, key, [])
def popitemlist(self):
"""Pop a ``(key, list)`` tuple from the dict."""
try:
return dict.popitem(self)
except KeyError, e:
raise self.KeyError(str(e))
def __init__(self, name=None):
self.name = name
super(LookupDict, self).__init__()
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
return '<lookup \'%s\'>' % (self.name)
def __getitem__(self, key):
# We allow fall-through here, so values default to None
return self.__dict__.get(key, None)
def get(self, key, default=None):
return self.__dict__.get(key, default)

72
requests/utils.py Normal file
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

9
setup.py Normal file → Executable file
View File

@@ -5,7 +5,10 @@ import os
import sys
import requests
from distutils.core import setup
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
@@ -25,12 +28,12 @@ if sys.version_info[:2] < (2,6):
setup(
name='requests',
version=requests.__version__,
description='Awesome Python HTTP Library that\'s actually usable.',
description='Python HTTP for Humans.',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='https://github.com/kennethreitz/requests',
url='http://python-requests.org',
packages= [
'requests',
'requests.packages',

View File

@@ -1,160 +1,494 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import with_statement
import unittest
import cookielib
try:
import omnijson as json
except ImportError:
import json
import requests
from requests.sessions import Session
HTTPBIN_URL = 'http://httpbin.ep.io/'
HTTPSBIN_URL = 'https://httpbin.ep.io/'
# HTTPBIN_URL = 'http://staging.httpbin.org/'
# HTTPSBIN_URL = 'https://httpbin-staging.ep.io/'
def httpbin(*suffix):
"""Returns url for HTTPBIN resource."""
return HTTPBIN_URL + '/'.join(suffix)
def httpsbin(*suffix):
"""Returns url for HTTPSBIN resource."""
return HTTPSBIN_URL + '/'.join(suffix)
SERVICES = (httpbin, httpsbin)
class RequestsTestSuite(unittest.TestCase):
"""Requests test cases."""
# It goes to eleven.
_multiprocess_can_split_ = True
def setUp(self):
pass
def tearDown(self):
"""Teardown."""
pass
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get('http://google.com')
r = requests.get(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
r = requests.get(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://www.google.com/search', params={'q': 'test'}, headers=heads)
r = requests.get(httpbin('user-agent'), headers=heads)
assert heads['User-agent'] in r.content
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://google.com/search?test=true', params={'q': 'test'}, headers=heads)
r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_user_agent_transfers(self):
"""Issue XX"""
heads = {'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'}
r = requests.get('http://whatsmyua.com', headers=heads);
heads = {
'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads);
self.assertTrue(heads['User-agent'] in r.content)
heads = {
'user-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads);
self.assertTrue(heads['user-agent'] in r.content)
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
r = requests.head(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_HEAD(self):
r = requests.head('https://google.com')
r = requests.head(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTPS_200_OK_GET(self):
auth = ('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
r = requests.get(url, auth=auth)
def test_HTTP_200_OK_PUT(self):
r = requests.put(httpbin('put'))
self.assertEqual(r.status_code, 200)
r = requests.get(url)
def test_HTTPS_200_OK_PUT(self):
r = requests.put(httpsbin('put'))
self.assertEqual(r.status_code, 200)
# reset auto authentication
requests.auth_manager.empty()
def test_HTTP_200_OK_PATCH(self):
r = requests.patch(httpbin('patch'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_PATCH(self):
r = requests.patch(httpsbin('patch'))
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTP_200_OK_GET(self):
for service in SERVICES:
auth = ('user', 'pass')
url = service('basic-auth', 'user', 'pass')
r = requests.get(url, auth=auth)
# print r.__dict__
self.assertEqual(r.status_code, 200)
r = requests.get(url)
self.assertEqual(r.status_code, 200)
def test_POSTBIN_GET_POST_FILES(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
for service in SERVICES:
post2 = requests.post(bin.url, files={'some': open('test_requests.py')})
self.assertEqual(post2.status_code, 201)
url = service('post')
post = requests.post(url).raise_for_status()
post = requests.post(url, data={'some': 'data'})
self.assertEqual(post.status_code, 200)
post2 = requests.post(url, files={'some': open('test_requests.py')})
self.assertEqual(post2.status_code, 200)
post3 = requests.post(url, data='[{"some": "json"}]')
self.assertEqual(post3.status_code, 200)
post3 = requests.post(bin.url, data='[{"some": "json"}]')
self.assertEqual(post.status_code, 201)
def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
for service in SERVICES:
post2 = requests.post(bin.url, files={'some': open('test_requests.py')}, data={'some': 'data'})
self.assertEqual(post2.status_code, 201)
url = service('post')
post = requests.post(url,
files={'some': open('test_requests.py')},
data={'some': 'data'})
self.assertEqual(post.status_code, 200)
def test_POSTBIN_GET_POST_FILES_WITH_HEADERS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')},
headers={'User-Agent': 'requests-tests'})
for service in SERVICES:
url = service('post')
post2 = requests.post(url,
files={'some': open('test_requests.py')},
headers = {'User-Agent': 'requests-tests'})
self.assertEqual(post2.status_code, 200)
self.assertEqual(post2.status_code, 201)
def test_nonzero_evaluation(self):
r = requests.get('http://google.com/some-404-url')
self.assertEqual(bool(r), False)
r = requests.get('http://google.com/')
self.assertEqual(bool(r), True)
for service in SERVICES:
r = requests.get(service('status', '500'))
self.assertEqual(bool(r), False)
r = requests.get(service('/'))
self.assertEqual(bool(r), True)
def test_request_ok_set(self):
r = requests.get('http://google.com/some-404-url')
self.assertEqual(r.ok, False)
for service in SERVICES:
r = requests.get(service('status', '404'))
self.assertEqual(r.ok, False)
def test_status_raising(self):
r = requests.get('http://google.com/some-404-url')
r = requests.get(httpbin('status', '404'))
self.assertRaises(requests.HTTPError, r.raise_for_status)
r = requests.get('http://google.com/')
r = requests.get(httpbin('status', '200'))
self.assertFalse(r.error)
r.raise_for_status()
def test_cookie_jar(self):
"""
.. todo:: This really doesn't test to make sure the cookie is working
"""
jar = cookielib.CookieJar()
self.assertFalse(jar)
requests.get('http://google.com', cookies=jar)
url = httpbin('cookies', 'set', 'requests_cookie', 'awesome')
r = requests.get(url, cookies=jar)
self.assertTrue(jar)
cookie_found = False
for cookie in jar:
if cookie.name == 'requests_cookie':
self.assertEquals(cookie.value, 'awesome')
cookie_found = True
self.assertTrue(cookie_found)
r = requests.get(httpbin('cookies'), cookies=jar)
self.assertTrue('awesome' in r.content)
def test_decompress_gzip(self):
r = requests.get('http://api.stackoverflow.com/1.1/users/495995/top-answer-tags')
r = requests.get(httpbin('gzip'))
r.content.decode('ascii')
def test_autoauth(self):
conv_auth = ('requeststest', 'requeststest')
requests.auth_manager.add_auth('convore.com', conv_auth)
r = requests.get('https://convore.com/api/account/verify.json')
self.assertEquals(r.status_code, 200)
def test_unicode_get(self):
requests.get('http://google.com', params={'foo': u'føø'})
requests.get('http://google.com', params={u'føø': u'føø'})
requests.get('http://google.com', params={'føø': 'føø'})
requests.get('http://google.com', params={'foo': u'foo'})
requests.get('http://google.com/ø', params={'foo': u'foo'})
for service in SERVICES:
url = service('/')
requests.get(url, params={'foo': u'føø'})
requests.get(url, params={u'føø': u'føø'})
requests.get(url, params={'føø': 'føø'})
requests.get(url, params={'foo': u'foo'})
requests.get(service('ø'), params={'foo': u'foo'})
def test_httpauth_recursion(self):
conv_auth = ('requeststest', 'bad_password')
r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
self.assertEquals(r.status_code, 401)
http_auth = ('user', 'BADpass')
for service in SERVICES:
r = requests.get(service('basic-auth', 'user', 'pass'), auth=http_auth)
self.assertEquals(r.status_code, 401)
def test_settings(self):
def test():
r = requests.get(httpbin(''))
r.raise_for_status()
with requests.settings(timeout=0.0000001):
self.assertRaises(requests.Timeout, test)
with requests.settings(timeout=100):
requests.get(httpbin(''))
def test_urlencoded_post_data(self):
for service in SERVICES:
r = requests.post(service('post'), data=dict(test='fooaowpeuf'))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), dict(test='fooaowpeuf'))
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_data(self):
for service in SERVICES:
r = requests.post(service('post'), data='fooaowpeuf')
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post'))
rbody = json.loads(r.content)
# Body wasn't valid url encoded data, so the server returns None as
# "form" and the raw body as "data".
self.assertEquals(rbody.get('form'), None)
self.assertEquals(rbody.get('data'), 'fooaowpeuf')
def test_urlencoded_post_querystring(self):
for service in SERVICES:
r = requests.post(service('post'), params=dict(test='fooaowpeuf'))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?test=fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), {}) # No form supplied
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_querystring(self):
for service in SERVICES:
r = requests.post(service('post'), params='fooaowpeuf')
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), {}) # No form supplied
self.assertEquals(rbody.get('data'), '')
def test_urlencoded_post_query_and_data(self):
for service in SERVICES:
r = requests.post(
service('post'),
params=dict(test='fooaowpeuf'),
data=dict(test2="foobar"))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?test=fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), dict(test2='foobar'))
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_query_and_data(self):
for service in SERVICES:
r = requests.post(service('post'),
params='fooaowpeuf', data="foobar")
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), None)
self.assertEquals(rbody.get('data'), 'foobar')
def test_idna(self):
r = requests.get(u'http://➡.ws/httpbin')
assert 'httpbin' in r.url
def test_urlencoded_get_query_multivalued_param(self):
for service in SERVICES:
r = requests.get(service('get'), params=dict(test=['foo','baz']))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.url, service('get?test=foo&test=baz'))
def test_urlencoded_post_querystring_multivalued(self):
for service in SERVICES:
r = requests.post(service('post'), params=dict(test=['foo','baz']))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?test=foo&test=baz'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), {}) # No form supplied
self.assertEquals(rbody.get('data'), '')
def test_urlencoded_post_query_multivalued_and_data(self):
for service in SERVICES:
r = requests.post(
service('post'),
params=dict(test=['foo','baz']),
data=dict(test2="foobar",test3=['foo','baz']))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, service('post?test=foo&test=baz'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), dict(test2='foobar',test3='foo'))
self.assertEquals(rbody.get('data'), '')
def test_GET_no_redirect(self):
for service in SERVICES:
r = requests.get(service('redirect', '3'), allow_redirects=False)
self.assertEquals(r.status_code, 302)
self.assertEquals(len(r.history), 0)
def test_HEAD_no_redirect(self):
for service in SERVICES:
r = requests.head(service('redirect', '3'), allow_redirects=False)
self.assertEquals(r.status_code, 302)
self.assertEquals(len(r.history), 0)
def test_redirect_history(self):
for service in SERVICES:
r = requests.get(service('redirect', '3'))
self.assertEquals(r.status_code, 200)
self.assertEquals(len(r.history), 3)
def test_relative_redirect_history(self):
for service in SERVICES:
r = requests.get(service('relative-redirect', '3'))
self.assertEquals(r.status_code, 200)
self.assertEquals(len(r.history), 3)
def test_session_HTTP_200_OK_GET(self):
s = Session()
r = s.get(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_session_HTTPS_200_OK_GET(self):
s = Session()
r = s.get(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_session_persistent_headers(self):
heads = {'User-agent': 'Mozilla/5.0'}
s = Session()
s.headers = heads
# Make 2 requests from Session object, should send header both times
r1 = s.get(httpbin('user-agent'))
assert heads['User-agent'] in r1.content
r2 = s.get(httpbin('user-agent'))
assert heads['User-agent'] in r2.content
self.assertEqual(r2.status_code, 200)
if __name__ == '__main__':

View File

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