Compare commits

...

331 Commits

Author SHA1 Message Date
Kenneth Reitz
569b59f495 v1.2.1 2013-05-20 16:10:23 -04:00
Kenneth Reitz
2afe3b2e99 Merge pull request #1370 from sigmavirus24/origin_req_host
Add an extra property for Python 3.3
2013-05-20 12:57:38 -07:00
Ian Cordasco
9ce7f0bf5b Attempt a fix for @michaelhelmick 2013-05-20 10:58:03 -04:00
Kenneth Reitz
7430a49f45 Merge remote-tracking branch 'origin/master' 2013-05-17 09:31:11 +02:00
Kenneth Reitz
538dbaecbe download image! 2013-05-17 09:31:01 +02:00
Kenneth Reitz
5943afa25d Merge pull request #1363 from dave-shawley/master
Fix for #1362
2013-05-16 23:23:04 -07:00
Dave Shawley
6e76ab7188 Fix for #1362.
`PreparedRequest.prepare_url` incorrectly applied IDNA encoding to the
URLs entire `netloc`.  It should only be encoding the hostname portion
of the URL.  IDNA encoding was limiting the user info, host, and port
segments to be a maximum of 63 characters which causes problems for
all by the most trivial user + password combinations.

- Replaced usage of `urlparse` in `PreparedRequest.prepare_url` with
  `urllib3` equivalent.
- Modified IDNA encoding section so that it only encodes the host
  portion of the URL.
2013-05-16 13:12:34 -04:00
Kenneth Reitz
eacb91afb1 Merge pull request #1327 from ambv/adapter_order
Fixes #1320: transport adapters stored in ordered form
2013-05-15 09:43:57 -07:00
Łukasz Langa
4c8af1fff4 Fixes #1320: transport adapters stored in ordered form 2013-05-15 13:34:09 +02:00
Kenneth Reitz
e7786ec14f Merge pull request #1355 from sigmavirus24/fix_links
Prevent a KeyError when there's no link headers
2013-05-09 01:03:49 -07:00
Ian Cordasco
88fda94218 Prevent a KeyError when there's no link headers 2013-05-08 23:26:49 -04:00
Kenneth Reitz
b599886beb Merge pull request #1352 from nicoddemus/docs-link-on-readme
Adding link to documentation in README.rst
2013-05-07 10:01:32 -07:00
Kenneth Reitz
7aedc6bb22 Merge pull request #1342 from mexicat/master
Fixed example in authentication docs
2013-05-07 10:01:18 -07:00
Bruno Oliveira
efefc6b778 Adding link to documentation in README.rst
Just adding a link to the documentation at python-requests.org
2013-05-06 23:46:59 -03:00
Kenneth Reitz
50e592209f Merge pull request #1347 from hobarrera/master
Issue #749: Add optional SNI support for python2.
2013-05-04 13:11:46 -07:00
Hugo Osvaldo Barrera
18857a0eed Issue #749: Add optional SNI support for python2.
SNI support will be enabled for python2 if ndg-httpsclient and pyopenssl are available.
2013-05-03 21:05:42 -03:00
giacomo
8781b56a0e Fixed example in authentication docs 2013-05-02 18:16:19 +03:00
Kenneth Reitz
e585e496c9 Merge pull request #1341 from cdunklau/docstring_test_1329
Minor update to tests, added docstrings
2013-05-01 11:40:02 -07:00
Colin Dunklau
f93275c47f Minor update to tests, added docstrings
Requested by @sigmavirus24.
2013-05-01 12:51:09 -05:00
Kenneth Reitz
4f83ef8328 Merge pull request #1314 from duailibe/master
Fix small typo in the Support docs
2013-05-01 10:29:40 -07:00
Kenneth Reitz
0f1bb96f01 Merge pull request #1337 from amercader/master
Fix minor issues on the API docs
2013-05-01 10:28:18 -07:00
Kenneth Reitz
e6e9b550c0 Merge pull request #1339 from cdunklau/case_insensitive_headers_cleanup
Rewrite CaseInsensitiveDict to work correctly/sanely
2013-05-01 10:27:58 -07:00
Kenneth Reitz
f8f1db8458 Merge pull request #1340 from ambv/tox_ini
Remove 3.1 and 3.2 from setup.py, add tox.ini to simplify testing on multiple versions
2013-05-01 10:27:02 -07:00
Łukasz Langa
7e825acd9b Remove Python 3.1 and 3.2 from the trove classifiers in setup.py 2013-05-01 19:26:12 +02:00
Colin Dunklau
f7596c75dc Rewrite CaseInsensitiveDict to work correctly/sanely
Fixes #649 and #1329 by making Session.headers a CaseInsensitiveDict,
and fixing the implementation of CID. Credit for the brilliant idea
to map `lowercased_key -> (cased_key, mapped_value)` goes to
@gazpachoking, thanks a bunch.

Changes from original implementation of CaseInsensitiveDict:

1.  CID is rewritten as a subclass of `collections.MutableMapping`.
2.  CID remembers the case of the last-set key, but `__setitem__`
    and `__delitem__` will handle keys without respect to case.
3.  CID returns the key case as remembered for the `keys`, `items`,
    and `__iter__` methods.
4.  Query operations (`__getitem__` and `__contains__`) are done in
    a case-insensitive manner: `cid['foo']` and `cid['FOO']` will
    return the same value.
5.  The constructor as well as `update` and `__eq__` have undefined
    behavior when given multiple keys that have the same `lower()`.
6.  The new method `lower_items` is like `iteritems`, but keys are
    all lowercased.
7.  CID raises `KeyError` for `__getitem__` as normal dicts do. The
    old implementation returned
6.  The `__repr__` now makes it obvious that it's not a normal dict.

See PR #1333 for the discussions that lead up to this implementation
2013-04-30 14:52:27 -05:00
amercader
505d2487e3 Fix API documentation issues
* 'Cookies' and 'Encodings' sections were not built because the
  reference to the functions was wrong.
* 'Exceptions' section had a wrong anchor link ('module-requests', same
   one as the main heading).
* Remove 'decode_gzip' function, which is no longer present.
2013-04-30 16:15:56 +02:00
Kenneth Reitz
ab36f3cc6f Merge pull request #1332 from bboe/urllib3
Update urllib3 to 59de03e6163c6928dc01832ed6e48e9f6c34c795.
2013-04-28 16:58:08 -07:00
Bryce Boe
49ca72c802 Update urllib3 to 59de03e6163c6928dc01832ed6e48e9f6c34c795.
This update includes two fixes:

* https://github.com/shazow/urllib3/issues/149
* https://github.com/shazow/urllib3/issues/174
2013-04-27 14:33:39 -07:00
Kenneth Reitz
d2ba5798ec Merge pull request #1330 from Kwpolska/patch-1
Fixing a tiny typo, noticed while working on the Polish translation
2013-04-27 04:53:37 -07:00
Chris Warrick
c3bef6b0d9 A tiny typo, noticed while working on the Polish translation. 2013-04-27 14:52:35 +03:00
Kenneth Reitz
98dea03710 Merge pull request #1328 from gazpachoking/session_cj_fix
Session CookieJar fix
2013-04-26 00:02:28 -07:00
Chase Sterling
0c609b59ab Fix session CookieJars without breaking more stuff this time 2013-04-25 23:15:50 -04:00
Chase Sterling
a7fef58756 Add another session cookie test 2013-04-25 23:11:43 -04:00
Chase Sterling
3ff8d43801 Fix cookielib import 2013-04-25 22:40:24 -04:00
Chase Sterling
9763a671da Fix crash when session.cookies was not a RequestsCookieJar 2013-04-25 22:34:51 -04:00
Chase Sterling
1866f7596e Add test for session cookiejars other than RequestsCookieJar 2013-04-25 22:32:03 -04:00
Kenneth Reitz
8f3f4e2744 milla 2013-04-20 00:22:13 -04:00
Kenneth Reitz
4438bfcc44 Merge remote-tracking branch 'origin/master' 2013-04-20 00:18:33 -04:00
Kenneth Reitz
65b6ef6036 pt 2013-04-20 00:18:27 -04:00
Lucas Duailibe
1ca423ec80 Fix small typo in the Support docs 2013-04-18 20:59:17 -03:00
Kenneth Reitz
770c3d7548 Merge pull request #1313 from iurisilvio/fix_max_redirects_docs
Fix max_redirects docs issue #1301
2013-04-17 12:39:03 -07:00
Iuri de Silvio
736e8cd735 Fix max_redirects docs issue #1301 2013-04-17 12:27:06 -07:00
Kenneth Reitz
6046fcffe8 Merge pull request #1279 from jemerick/unicode_multipart_post
Unicode strings in multipart post requests
2013-04-16 12:55:58 -07:00
Kenneth Reitz
d13bf9f113 support for draft-tbray-http-legally-restricted-status-02
http://datatracker.ietf.org/doc/draft-tbray-http-legally-restricted-stat
us/?include_text=1
2013-04-15 22:47:20 -04:00
Kenneth Reitz
ae5419db17 translations 2013-04-15 03:22:56 -04:00
Kenneth Reitz
9c2f9f61ef Translatoins 2013-04-15 03:22:48 -04:00
Kenneth Reitz
636476e704 Merge remote-tracking branch 'origin/master' 2013-04-15 03:21:40 -04:00
Kenneth Reitz
aafca7acdc translations 2013-04-15 03:21:32 -04:00
Kenneth Reitz
a527ecfabd Merge pull request #1242 from oviboy/master
HTTP Digest Auth case insensitive replacement of "Digest "
2013-04-13 20:28:43 -07:00
Kenneth Reitz
967cfbe663 Merge pull request #1299 from schlamar/fix-decompression
Use streaming decompression feature of urllib3.
2013-04-13 20:27:46 -07:00
Kenneth Reitz
9df450193f Merge pull request #1302 from ssbarnea/master
Documents the actual logging methods #1297
2013-04-13 20:25:47 -07:00
Kenneth Reitz
082029ff2e switch streaming requests to httpbin 2013-04-13 15:26:49 -04:00
Kenneth Reitz
c0baec1431 Merge pull request #1305 from michaelhelmick/patch-1
Fix Advanced docs Twitter Streaming example
2013-04-13 12:24:21 -07:00
Kenneth Reitz
561366cab0 Merge pull request #1309 from toastdriven/better-max-retries
Changed HTTPAdapter to allow max retries to be specified when initializing.
2013-04-13 12:05:13 -07:00
Kenneth Reitz
f0950a9e6c Merge pull request #1311 from sigmavirus24/fix1303
Change the method when it isn't already GET/HEAD
2013-04-13 12:02:29 -07:00
Ian Cordasco
c5d0a0931e Change the method when it isn't already GET/HEAD
For some reason it was only change the method when a POST was being made. This
is almost certainly my fault.

Fixes #1303
2013-04-13 12:31:22 -04:00
Daniel Lindsley
36dcce1a06 Changed HTTPAdapter to allow max retries to be specified when initializing. 2013-04-12 16:40:39 -07:00
Mike Helmick
ed0242ae3a Fix Advanced docs Twitter Streaming example
Twitter API v1.1 Streaming now requires OAuth Authentication rather than XAuth.
I believe v1 is being blacked out sometime at the beginning of May.
2013-04-11 20:19:51 -04:00
Kenneth Reitz
eda4b55f7a Merge pull request #1306 from pombredanne/patch-1
Updated NOTICE copyright from 2022 to 2012
2013-04-11 09:04:07 -07:00
pombredanne
6e3cfe5dd5 Updated NOTICE copyright from 2022 to 2012
Which is likely what you meant, eh?
2013-04-11 17:08:27 +03:00
Sorin Sbarnea
fb49481ddf Updated documentation indicating that logging is done via requests.packages.urllib3 instead of requests.
modified:   docs/api.rst
2013-04-10 16:46:05 +01:00
Sorin Sbarnea
17ecb6891c * Documented the logging, requested in #1297
* Added build directory and *.egg to .gitignore
* Added sphinx as setup requirement in order to be able to build documentation with `pyhton setup.py build_sphinx`

modified:   .gitignore
modified:   docs/api.rst
modified:   setup.py
2013-04-10 16:15:28 +01:00
schlamar
4c3432b759 Fix test with StringIO. 2013-04-10 08:24:33 +02:00
schlamar
59f916ca4a Use streaming decompression feature of urllib3. 2013-04-10 08:08:33 +02:00
schlamar
6d6252aa9f Update urllib3 to 71f84f9. 2013-04-10 08:00:36 +02:00
Kenneth Reitz
2426eeb371 Merge pull request #1298 from Lukasa/adapter_doc
Transport Adapter Docs
2013-04-09 13:17:19 -07:00
Cory Benfield
e5f1053b30 Shell of Transport Adapter documentation. 2013-04-09 20:58:59 +01:00
Cory Benfield
c73f653352 Add HTTPAdapter to API docs. 2013-04-09 19:54:47 +01:00
Kenneth Reitz
5b937b384b Merge pull request #1296 from sursh/master
Add helpful error message to r.json() method
2013-04-08 11:01:36 -07:00
Sasha Laundy
8ebeb3dc3f Make json error message more specific 2013-04-08 13:03:13 -04:00
sursh
dfa59c2d97 Fix markdown 2013-04-08 13:01:02 -03:00
sursh
511cc4c034 Make json error on empty response more specific 2013-04-08 13:00:27 -03:00
Kenneth Reitz
fa9234da05 Merge pull request #1295 from sigmavirus24/issue1293
Fix #1293
2013-04-06 17:27:23 -07:00
Ian Cordasco
afcc883d7f Fix #1293 2013-04-06 11:26:52 -04:00
Kenneth Reitz
1e465fd255 Merge pull request #1291 from gazpachoking/dont_set_session_cookies_on_response
Don't set all session cookies on response.cookies
2013-04-04 20:41:52 -07:00
Chase Sterling
49a3664222 Don't set all session cookies on response.cookies 2013-04-04 23:30:52 -04:00
Kenneth Reitz
848f2c297e Merge pull request #1290 from gazpachoking/#1287
fix #1287: Make sure expired cookies get removed from session.cookies
2013-04-04 20:22:38 -07:00
Chase Sterling
5c47ce1136 Make sure unit test works on python 2.6 2013-04-04 22:48:14 -04:00
Chase Sterling
88f13598f3 Add a unit test for server expiring cookies from session 2013-04-04 22:40:27 -04:00
Chase Sterling
d22ac00098 fix #1287: Make sure expired cookies get removed from session.cookies 2013-04-04 22:11:38 -04:00
Kenneth Reitz
dccfc5ba3c Merge pull request #1283 from sigmavirus24/master
Closes #1280
2013-04-03 18:04:40 -07:00
Jason Emerick
e7247ce3f6 model the encode_files data handling after encode_params 2013-04-02 14:22:49 -04:00
Jason Emerick
168c3e6913 add a few more variations to the unicode multipart post test 2013-04-02 14:22:12 -04:00
Jason Emerick
82d36d8259 add additional test for unicode multipart post 2013-04-02 11:41:07 -04:00
Ian Cordasco
b9e5cce2d2 Add PreparedRequest recipe to the docs 2013-04-02 10:07:37 -04:00
Ian Cordasco
1abd13700b Closes #1280
Correct the doc-string for Session#request that I copied without thinking
about.
2013-04-02 09:27:25 -04:00
Kenneth Reitz
54ed5ed469 Revert "Fix for the issue https://github.com/kennethreitz/requests/issues/1280"
This reverts commit ca0aea640d.
2013-04-02 08:13:46 -04:00
Kenneth Reitz
cdb700737b Merge pull request #1281 from KamilSzot/Issue_kennethreitz_requests_issues_1280
Fix for the issue https://github.com/kennethreitz/requests/issues/1280
2013-04-02 04:44:59 -07:00
Kamil Szot
ca0aea640d Fix for the issue https://github.com/kennethreitz/requests/issues/1280 2013-04-02 13:42:40 +02:00
Kenneth Reitz
20a8a9b681 Merge pull request #1278 from t-8ch/patch-1
fix tiny typo in HISTORY.rst
2013-04-01 21:36:49 -07:00
Kenneth Reitz
9c18febf45 Merge pull request #1277 from pborreli/typos
Fixed typos
2013-04-01 21:36:39 -07:00
Kenneth Reitz
848aca21be Merge pull request #1276 from alex/patch-1
is should not be used for comparing numbers
2013-04-01 20:42:22 -07:00
Jason Emerick
f37b968475 use compat.str instead of compat.builtin_str 2013-04-01 18:14:52 -04:00
Jason Emerick
f0660e33a2 add test for unicode multipart post 2013-04-01 18:10:12 -04:00
Thomas Weißschuh
333fa87489 fix tiny typo in HISTORY.rst 2013-04-01 18:47:20 +00:00
Pascal Borreli
037b38badb Fixed typos 2013-04-01 18:02:18 +00:00
Alex Gaynor
39acf1dbd2 is should not be used for comparing numbers 2013-03-31 23:20:46 -07:00
Kenneth Reitz
59b69d1fb8 Merge remote-tracking branch 'origin/master' 2013-03-31 08:28:57 +03:00
Kenneth Reitz
d06908d655 v1.2.0 2013-03-31 08:28:22 +03:00
Kenneth Reitz
5b5ffc9714 Merge pull request #1268 from t-8ch/update_urllib3
update vendored urllib3
2013-03-30 22:27:59 -07:00
Kenneth Reitz
75703d57e6 fix syntax error 2013-03-31 08:22:44 +03:00
Kenneth Reitz
bd6b981d12 Merge pull request #1267 from sigmavirus24/master
One last pull request before 1.2 ideally
2013-03-30 22:21:52 -07:00
Kenneth Reitz
6a2eea8b21 Merge pull request #1270 from makto/add_attr
add 'max_redirects' to Session's __attrs__
2013-03-30 22:20:02 -07:00
Ian Cordasco
325ea7b7e2 Use session defaults instead of arbitrary ones 2013-03-29 20:18:58 -04:00
makto
4ffae38627 add 'max_redirects' to Session's __attrs__ to ensure proper serialization of Session 2013-03-29 20:58:15 +08:00
Thomas Weißschuh
d7908a9fde update vendored urllib3 2013-03-28 12:49:04 +00:00
Ian Cordasco
0cd23d8d6e Fix the tests and unseparate comments from code
See the comments on the previous few commits on GitHub.
2013-03-28 08:33:34 -04:00
Ian Cordasco
aca91e06f2 Restore Session.request docstring
Resolves #1251
2013-03-27 23:46:21 -04:00
Ian Cordasco
88177ec33f Finally resolve #1084
Send body on redirect when POSTing or PUTing.
2013-03-27 23:30:00 -04:00
Ian Cordasco
1d5c4f3f0f This should take care of #1266
We were sending 'None' as the Content-Length on requests where the body was a
generator. This commit should prevent that entirely.
2013-03-27 23:26:11 -04:00
Ian Cordasco
478d49027f Add correct defaults in Session.send()
Resolves #1258

Also fixed the tests to reflect the necessary changes.
2013-03-27 23:17:34 -04:00
Ian Cordasco
46a770e03f Update the HISTORY for v1.2 2013-03-27 23:10:59 -04:00
Kenneth Reitz
ba25184ed5 sp 2013-03-26 16:38:11 -04:00
Kenneth Reitz
7f1ad5c127 Merge remote-tracking branch 'origin/master' 2013-03-26 16:32:25 -04:00
Kenneth Reitz
ab19b79375 Rezzy the Request Sea Turtle 2013-03-26 16:32:20 -04:00
Kenneth Reitz
c63472e328 Merge pull request #1263 from justin-factual/master
iter_lines documentation for #1260
2013-03-25 12:48:45 -07:00
Justin Fenn
6963b8490c Clarify streaming behavior in iter_lines doc 2013-03-25 12:20:33 -07:00
Ovidiu Negrut
9d16c72767 compiled regex expression in digest auth, this also works in python 2.6.x 2013-03-25 12:28:25 +02:00
Ovidiu Negrut
186a589783 Merge branch 'master' of git://github.com/kennethreitz/requests 2013-03-25 12:26:45 +02:00
Kenneth Reitz
12ef43e407 /s/make/invoke 2013-03-22 15:52:08 -04:00
Kenneth Reitz
92f7478174 first 2013-03-22 15:48:27 -04:00
Kenneth Reitz
19d38d502f actually cleanup url authentication 2013-03-22 15:47:20 -04:00
Kenneth Reitz
52d328ec3c cp949prover for charade 2013-03-22 15:21:28 -04:00
Kenneth Reitz
a554828931 invoke! 2013-03-22 15:21:04 -04:00
Kenneth Reitz
80a861cb84 upgrade to charade v1.0.3 2013-03-22 15:20:49 -04:00
Kenneth Reitz
0d07d1afb3 remove makefile 2013-03-22 15:20:27 -04:00
Kenneth Reitz
030b9763b5 fix get_auth_from_url 2013-03-22 00:15:06 -04:00
Kenneth Reitz
03a3ca5004 get_auth_from_url return None if nothing is found 2013-03-22 00:13:08 -04:00
Kenneth Reitz
1325409560 simplify get_auth_from_url call 2013-03-22 00:12:58 -04:00
Kenneth Reitz
13aeb9cb06 Merge pull request #1254 from jkakar/url-authentication
url-authentication
2013-03-21 21:06:23 -07:00
Jamu Kakar
7d217bf9bd - Pull credentials out of the URL when possible. 2013-03-21 17:55:08 -07:00
Kenneth Reitz
cb2116dcce Merge pull request #1239 from miikka/fix-issue-1228
Use session cookies when following redirects
2013-03-20 03:37:25 -07:00
Kenneth Reitz
db90d85a8d Merge pull request #1245 from Damgaard/master
Remove duplicate comment.
2013-03-14 08:29:53 -07:00
Andreas Damgaard Pedersen
af791d48c6 Remove duplicate comment. 2013-03-14 16:12:45 +01:00
Miikka Koskinen
120a2f385a Do not pass cookies to resolve_redirects
SessionRedirectMixin is extending Session, so we can just use
self.cookies.
2013-03-12 18:45:11 +02:00
Miikka Koskinen
5bb2be9a23 Use session cookies when following redirects
When a redirect was followed, only the cookies set by the initial
response were used in the follow-up request. Fixes #1228.
2013-03-12 18:43:58 +02:00
Miikka Koskinen
e958511df0 Add failing test case for #1228 2013-03-12 18:43:58 +02:00
Kenneth Reitz
1642996798 Merge pull request #1244 from jajadinimueter/master
Fixed further pickeling issues in Session and HTTPAdapter
2013-03-12 08:23:21 -07:00
Florian Mueller
c41932e184 Fixed some pickeling issues with HTTPAdapter and Session
Added trust_env and stream to Session.__attrs__. Initialize
self._pool_connections and self._pool_maxsize in HTTPAdapter.
2013-03-12 16:04:19 +01:00
Kenneth Reitz
25ea8cdb38 Merge pull request #1243 from darjus-amzn/master
Pickling of Session and HTTPAdapter + a test
2013-03-12 07:37:55 -07:00
Darjus Loktevic
e706d18cf8 Pickling of Session and HTTPAdapter + a test
This is for issue #1088
2013-03-11 18:12:34 +00:00
Ovidiu Negrut
e752455b6f Digest auth: case insensitive replacement of 'digest ' string with '' from WWW-Authenticate 2013-03-11 10:28:37 +02:00
Kenneth Reitz
3e72f234d0 Merge pull request #1238 from oczkers/master
cleanup (remove max_retries test)
2013-03-08 11:51:42 -08:00
Piotr Staroszczyk
25dc07e8ed cleanup (remove max_retries test) 2013-03-08 11:17:17 +01:00
Kenneth Reitz
13de69299f Merge pull request #1234 from Vassius/issue-1225
Fix issue #1225 (Documentation: response object status/reason)
2013-03-05 07:31:10 -08:00
Markus Wiik
90e109c241 Fix issue #1225 (Documentation: response object status/reason) 2013-03-05 16:18:24 +01:00
Kenneth Reitz
d25ba77b2e Merge remote-tracking branch 'origin/master' 2013-03-04 17:54:32 -05:00
Kenneth Reitz
c0d4b23cea Merge branch 'httperror_init' of git://github.com/dmedvinsky/requests
Conflicts:
	AUTHORS.rst
	test_requests.py
2013-03-04 17:54:25 -05:00
Kenneth Reitz
99eead01a3 Merge pull request #1223 from andrewjesaitis/master
Fixes __getstate__ for session pickling
2013-03-04 14:52:31 -08:00
Andrew Jesaitis
d60845303b Adds __attrs__ back to Session object 2013-03-04 11:07:29 -07:00
Kenneth Reitz
b14584f36e Revert "[kennethreitz/requests#1208] adding unit test for max_retries"
This reverts commit 18b29ea005.
2013-03-03 12:01:47 -05:00
Kenneth Reitz
23d8522285 Revert "[kennethreitz/requests#1208] adding a max_retries argument"
This reverts commit 796d3225dd.
2013-03-03 12:01:38 -05:00
Dmitry Medvinsky
c4f9340fb4 Add ability to pass response to HTTPError()
Just a little refactoring, but it seems nicer to me to be able to pass
the response when constructing the `HTTPError` instance instead of
constructing it and then changing the member variable.
2013-03-03 10:05:42 +04:00
Kenneth Reitz
d372a5b10b Merge pull request #1190 from mkomitee/master
Pass user options to hooks
2013-03-02 13:04:00 -08:00
Kenneth Reitz
7aad3fd08c Merge pull request #1219 from Wilfred/master
Adding max_retries as an argument
2013-03-02 13:02:26 -08:00
Kenneth Reitz
76c4b68e4a Merge pull request #1226 from dmedvinsky/fix-typos
Fix couple of typos in HISTORY.rst
2013-03-02 13:00:48 -08:00
Kenneth Reitz
07441c8951 Merge pull request #1229 from sprt/master
Doc fix: URLError doesn't exist anymore
2013-03-02 12:59:41 -08:00
Kenneth Reitz
98ff29f793 Merge pull request #1230 from davidfischer/trivial-docs-fix
Github URL fix
2013-03-02 12:59:10 -08:00
Kenneth Reitz
08cab48431 Merge pull request #1231 from davidfischer/docs-migration1.0
Initial docs patch for migrating from pre 1.0
2013-03-02 12:58:49 -08:00
David Fischer
7e7c275504 Session isn't advertised as a context manager 2013-03-02 08:59:19 -08:00
David Fischer
73abf84ace Capitalize s in Session 2013-03-02 08:51:55 -08:00
David Fischer
7eba5ffe48 Logic on streaming responses was changed in 1.0
* prefetch=False in 0.x is now stream=True
2013-03-02 08:44:14 -08:00
David Fischer
1d2ee524d8 Initial docs patch for migrating from pre 1.0 2013-03-01 23:21:08 -08:00
David Fischer
38f2581d6f Github URL fix 2013-03-01 20:36:42 -08:00
sprt
b80f8aa475 URLError doesn't exist anymore 2013-03-01 20:20:53 +01:00
Dmitry Medvinsky
057f32924a Fix couple of typos in HISTORY.rst
One typo and a couple of auto-completions, I guess.
2013-03-01 11:42:43 +04:00
Andrew Jesaitis
6a0845c984 Checks __attrs__ on session instance prior to iterating. 2013-02-28 15:13:57 -07:00
Wilfred Hughes
18b29ea005 [kennethreitz/requests#1208] adding unit test for max_retries 2013-02-27 16:01:36 +00:00
Wilfred Hughes
796d3225dd [kennethreitz/requests#1208] adding a max_retries argument 2013-02-27 16:01:23 +00:00
Ian Cordasco
be62645dd5 Revert "If Content-Length is already set.."[1]
This reverts commit 544d08d0f6.

[1]"If Content-Length is already set, don't over ride it"
2013-02-25 09:29:05 -05:00
Kenneth Reitz
603fd42fe6 Merge pull request #1205 from sigmavirus24/fix1203
Missing line was allowing redirects with HEAD
2013-02-25 02:31:21 -08:00
Kenneth Reitz
1fc567449c Merge pull request #1210 from Lukasa/urlencode_proxy
Unquote proxy usernames and passwords.
2013-02-25 02:31:00 -08:00
Kenneth Reitz
c01bc5be5b Revert "Lukasa is lazy"
This reverts commit 178ff62b93.
2013-02-22 08:23:34 -05:00
Cory Benfield
577dba2bf7 Unquote proxy usernames and passwords. 2013-02-22 11:33:01 +11:00
Ian Cordasco
a788cb7271 Missing line was allowing redirects with HEAD
Closes #1203
2013-02-20 08:57:37 -05:00
Kenneth Reitz
178ff62b93 Lukasa is lazy 2013-02-20 02:11:41 -05:00
Kenneth Reitz
aa99525537 Merge pull request #1193 from Lukasa/timeout_exception
Throw more informative exceptions.
2013-02-19 23:10:44 -08:00
Kenneth Reitz
7e0b9cf409 Merge pull request #1200 from gazpachoking/session_cookies_fix
Make sure session cookies do not overwrite explicit request cookies
2013-02-19 22:50:10 -08:00
Kenneth Reitz
017c027629 Merge pull request #1199 from t-8ch/docs_explicit_proxy_scheme
Use explicit scheme for proxies in the docs
2013-02-19 05:40:15 -08:00
Cory Benfield
c2480f65e6 Rethrow underlying exceptions. 2013-02-18 16:30:16 +11:00
Chase Sterling
3f86e22a07 Make sure session cookies do not overwrite explicit request cookies
Implement RequestsCookieJar.copy
Use RequestsCookieJar.update when merging cookiejars
2013-02-16 00:56:59 -05:00
Thomas Weißschuh
b53975327f use explicit scheme for proxies in the docs
Issue #1192 tried to force user to provide a scheme for proxy urls.
As this would break backwards compability change the docs instead.
2013-02-15 16:32:50 +00:00
Kenneth Reitz
d0390d4f27 Merge pull request #1194 from gazpachoking/cookiejar_update
Allow RequestsCookieJar to be updated with cookies from a CookieJar
2013-02-14 22:42:49 -08:00
Chase Sterling
f3393fb24c Remove ability to from RequestCookieJar __getitem__, __setitem__ to use cookies as keys 2013-02-14 22:52:31 -05:00
Michael Komitee
4c21106222 Fixing test for python3 2013-02-14 21:33:01 -05:00
Chase Sterling
87d9d9643c Allow RequestsCookieJar to be updated with cookies from a CookieJar 2013-02-14 01:05:42 -05:00
Michael Komitee
df5dcb8a7d New tests fail on python 3.x because read() returns bytes and the test checks for strings 2013-02-13 22:42:56 -05:00
Michael Komitee
69ba64380b Adding test to ensure options like stream function with authentication
This test demonstrates the reason why we need to pass kwargs to hooks. Without
it, features like stream cannot work with authentication.
2013-02-13 21:28:32 -05:00
Michael Komitee
d0285fac42 Use user supplied options when resending authenticated requests
Hooks sometimes have to send requests (e.g. when responding to a 401 during
authentication).

All keyword arguments should be passed along when hooks are dispatched so that
if a user wanted to use a timeout, stream, specify a cert location with the
verify flag, etc, their specification can be followed.
2013-02-13 19:11:38 -05:00
Kenneth Reitz
f73bda06e9 Merge pull request #1185 from sigmavirus24/fix_hook_dispatching
Dispatch hooks before following redirects
2013-02-13 02:13:31 -08:00
Ian Cordasco
4dfd6f3fc1 Dispatch hooks before following redirects
Fixes #1183
2013-02-12 23:00:06 -05:00
Kenneth Reitz
cdec20af65 Merge pull request #1181 from denis-ryzhkov/master
Fix of UnicodeDecodeError on unicode header name that can be converted to ascii.
2013-02-12 08:43:02 -08:00
Kenneth Reitz
09a7251245 Merge pull request #1184 from piotr-dobrogost/numeric_codes
small cleanup of redirect codes
2013-02-12 00:27:05 -08:00
Denis Ryzhkov
56f4b7ca68 Deleted is_py2 check from unicode_header_name fix thanks to Lukasa. 2013-02-12 09:51:46 +03:00
Piotr Dobrogost
4c8d1b9a7d removed no longer used redirect codes from models
added numeric values of redirect codes in comments
2013-02-11 23:07:12 +01:00
Denis Ryzhkov
6da7e22a4a Fix of UnicodeDecodeError on unicode header name that can be converted to ascii. 2013-02-11 15:37:58 +03:00
Kenneth Reitz
5d9fcc711b Merge pull request #1180 from sigmavirus24/master
Fix Session level Cookie Handling
2013-02-10 16:43:21 -08:00
Ian Cordasco
2e31696156 Test and perfection for cookie handling.
I also fixed up some of the RequestsCookieJar methods so using
jar.update(other_jar) works without a problem. This cleans up some of the code
in sessions and the resolve_redirects method.
2013-02-10 19:36:36 -05:00
Ian Cordasco
0fb13e0b6c And tests 2013-02-10 17:49:49 -05:00
Kenneth Reitz
1da1490bcd urllib3 update
#1053
2013-02-10 17:43:58 -05:00
Ian Cordasco
9cdc8325ae Fix Setting a cookie on redirect 2013-02-10 17:43:37 -05:00
Kenneth Reitz
12d66cfc41 Merge pull request #1173 from sigmavirus24/use_send_in_resolve_redirects
Use send in resolve redirects
2013-02-10 14:21:13 -08:00
Ian Cordasco
9c8660dbb6 Resolve @piotr-dobrogost's concerns
Piotr had good objections to my not re-sending the body of the request on 307.
2013-02-10 17:14:45 -05:00
Ian Cordasco
e7bc9bf1b2 Preserve the original request.
Let's make a copy to preserve it.
2013-02-10 17:11:16 -05:00
Ian Cordasco
e2ad0d0fe8 We shouldn't be sending the data on redirect.
As such, we should remove the body from the old request as well as the
Content-Length header.
2013-02-10 17:11:16 -05:00
Ian Cordasco
3299771e34 Fix the potential issue mentioned in #1151
See: https://github.com/kennethreitz/requests/pull/1151#issuecomment-12905796

This is solved by just reusing the PreparedRequest from the last request.
2013-02-10 17:11:16 -05:00
Kenneth Reitz
f2be5fae27 Merge pull request #1161 from sigmavirus24/fix1159
If Content-Length is already set, don't override
2013-02-10 14:07:08 -08:00
Kenneth Reitz
90cd37fda0 Merge pull request #1119 from Lukasa/diags
Better diagnostics when trying to send unprepared request
2013-02-10 14:06:38 -08:00
Cory Benfield
b0657cf163 Guard against users sending unprepared requests. 2013-02-10 06:13:02 +00:00
Kenneth Reitz
03f6908fe9 Merge pull request #1162 from Lukasa/proxy_auth
Proxy Authorization Headers
2013-02-06 13:05:04 -08:00
Kenneth Reitz
c68f928254 Merge pull request #1168 from Lukasa/freeze
Feature freeze.
2013-02-06 11:53:55 -08:00
Cory Benfield
ff954e16f1 Feature freeze. 2013-02-06 19:01:24 +00:00
Cory Benfield
d437c338d0 Add Proxy-Auth header when proxies have credentials. 2013-02-06 18:40:35 +00:00
Kenneth Reitz
9fc7059140 Merge pull request #1151 from sigmavirus24/fix1133
Move redirect handling from Session.request to Session.send
2013-02-05 11:42:51 -08:00
Ian Cordasco
544d08d0f6 If Content-Length is already set, don't override
Re: #1159
2013-02-01 11:58:23 -05:00
Kenneth Reitz
ae55676a50 Merge pull request #1158 from yehudasa/master
Fix check that breaks handling of 303 response in some cases (v2)
2013-02-01 07:30:47 -08:00
Yehuda Sadeh
b07c1ebd85 Fix POST 303 redirect resonse handling
This fixes issue #1156.

Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
2013-01-31 11:23:09 -08:00
Kenneth Reitz
113b67c069 moar 2013-01-31 14:35:42 +00:00
Kenneth Reitz
12246afab8 HMG 2013-01-31 13:20:14 +00:00
Kenneth Reitz
85da715f40 Merge pull request #1153 from cbare/master
use only base filename in multipart file upload
2013-01-30 15:56:21 -08:00
Kenneth Reitz
b554b0c1cb Merge pull request #1152 from ekatsah/patch-1
Update docs/user/advanced.rst
2013-01-30 15:54:20 -08:00
Christopher Bare
9ff8cbe5fb shorten filename by os.path.basename in guess_filename(obj) 2013-01-30 15:33:43 -08:00
ekatsah
79727abb32 Update docs/user/advanced.rst
Rephrasing a line in the docs, add explicite mention of "client side certificate" (for indexing and clarity)
2013-01-30 21:22:24 +01:00
Ian Cordasco
ba485913b6 Thanks to @maxcountryman for the code review 2013-01-30 10:58:32 -05:00
Ian Cordasco
a95bfd3032 Fix the elapsed test and #1133 2013-01-29 23:50:37 -05:00
Kenneth Reitz
207a2744aa Merge pull request #1143 from Lukasa/master
Response as iterator
2013-01-28 20:52:17 -08:00
Cory Benfield
95084c9ca8 Fix test for Py3. 2013-01-28 18:35:41 +00:00
Cory Benfield
2f7923bc05 More useful docstring. 2013-01-28 18:35:41 +00:00
Cory Benfield
2ac3913733 Make Response objects iterable. 2013-01-28 18:35:41 +00:00
Kenneth Reitz
231e343a3b Merge pull request #1148 from sigmavirus24/timing_switch
Move the timing work to Session.send
2013-01-28 09:40:10 -08:00
Ian Cordasco
c8ad4f0b73 Move the timing work to Session.send
per @kennethreitz's request
2013-01-28 11:53:02 -05:00
Kenneth Reitz
25545b393b Merge pull request #1146 from oczkers/master
It`s more pep8 now
2013-01-27 19:11:15 -08:00
Kenneth Reitz
dfa89e299a Merge pull request #1138 from clee/master
Time each request
2013-01-27 19:10:16 -08:00
Chris Lee
318300848f Use timedelta and change attribute name back to elapsed 2013-01-27 19:07:48 -08:00
Chris Lee
d4aaef1e9a Time how long each request takes
Stored as attribute Response.time_taken
2013-01-27 19:06:50 -08:00
oczkers
40a060cf57 it`s more pep8 now 2013-01-27 02:04:12 +01:00
Kenneth Reitz
ac5e6807fa Merge pull request #1145 from sigmavirus24/master
Mock the links test
2013-01-26 09:05:19 -08:00
Ian Cordasco
250896b7f3 Mock the links test. 2013-01-26 11:55:08 -05:00
Kenneth Reitz
9d1d927420 Merge pull request #1103 from dmckeone/master
Restore the ability to use a list of 2-tuples for the data keyword argument with requests.post.
2013-01-25 21:02:01 -08:00
Kenneth Reitz
440aeca5d4 Merge pull request #1123 from andrewjesaitis/master
Fixes DigestAuth with Sessions
2013-01-25 21:00:33 -08:00
Kenneth Reitz
b6af86225d Merge pull request #1140 from ralphbean/cookie-test
Test for cookie parameters.
2013-01-25 20:58:35 -08:00
Kenneth Reitz
c50f8f7cc8 Merge pull request #1142 from theaeolianmachine/removeGETContentLength
Remove default Content-Length from GET requests.
2013-01-25 20:58:20 -08:00
Johnny Goodnow
8d8865aadb Remove Content-Length from GET/HEAD by default.
Now, Content-Length is only auto-added for non-GET/HEAD requests.
2013-01-25 20:50:58 -08:00
Johnny Goodnow
f7c10ca74d Always add Content-Length to HTTP PATCH. 2013-01-25 12:07:24 -08:00
Johnny Goodnow
4cac9f07c4 Add myself to AUTHORS. 2013-01-25 00:19:09 -08:00
Johnny Goodnow
f453892960 Fix python 2.6 unittest compatability issue. 2013-01-24 21:25:14 -08:00
Johnny Goodnow
44b1e7ebea Remove default Content-Length from GET requests.
Fix #1051.
2013-01-24 21:13:32 -08:00
Ralph Bean
4ce6440795 purported py2.6 support for cookies test. 2013-01-25 00:10:51 -05:00
Kenneth Reitz
22623bd8c2 Merge pull request #1099 from sprt/master
Make hooks work with prepared requests
2013-01-24 10:00:24 -08:00
Ralph Bean
5861254d8f Test for cookie parameters. 2013-01-24 12:47:55 -05:00
sprt
ab2e7df8a9 Merge branch 'master' of git://github.com/kennethreitz/requests
Conflicts:
	test_requests.py
2013-01-24 18:36:53 +01:00
Kenneth Reitz
beec48c8f8 Merge pull request #1137 from whit537/unperson_test
Remove a test per @kennethreitz in IRC
2013-01-23 17:37:13 -08:00
Kenneth Reitz
b2427223c1 Merge pull request #1136 from steveklabnik/test_link_header
introduce a test for link headers.
2013-01-23 17:34:55 -08:00
Chad Whitacre
ef52044c5f Remove a test per @kennethreitz in IRC
The test suite is moving from the httpbin pattern (which hits the
network) to depending on the request.prepare method (which doesn't).
Here's a start ...
2013-01-23 20:32:03 -05:00
Steve Klabnik
ccea28db07 introduce a test for link headers. 2013-01-23 20:31:05 -05:00
Kenneth Reitz
e21ef547f7 Merge pull request #1134 from steveklabnik/remove_silly_assert
remove silly assert
2013-01-23 17:06:40 -08:00
Steve Klabnik
0af8ff9405 remove silly assert 2013-01-23 20:03:43 -05:00
Kenneth Reitz
6e0ca583fe Merge pull request #1132 from sburns/jsonkwargs
Pass kwargs to json.loads
2013-01-23 14:02:37 -08:00
Scott Burns
2bcc4a774a Pass kwargs to json.loads
Not all JSON is created equally. This commit
addresses when users want to take control of the
json decode process.
2013-01-23 15:25:48 -06:00
Andrew Jesaitis
34268b16c3 Moves num_401_calls counter to HTTPDigestAuth 2013-01-23 10:32:56 -07:00
Ian Cordasco
1cfe59299b Only call the hook once. 2013-01-23 11:51:37 -05:00
Ian Cordasco
e771aa9386 Comment & replace something that keeps disappearing 2013-01-23 11:28:30 -05:00
sprt
cc7bee19f0 Refactor prepare_hooks() 2013-01-23 15:45:44 +01:00
sprt
088f908d58 Add missing import 2013-01-23 15:45:14 +01:00
sprt
c4ad8afba9 Merge remote-tracking branch 'upstream/master'
Conflicts:
	requests/sessions.py
2013-01-23 14:42:45 +01:00
Kenneth Reitz
0c09d16b29 Merge pull request #1125 from juanriaza/master
HTTPDigestAuth: missing algorithm field
2013-01-22 19:43:50 -08:00
Kenneth Reitz
30573b2d8b Merge pull request #1116 from Lukasa/master
Correct type of Content-Length headers.
2013-01-22 18:51:17 -08:00
Kenneth Reitz
1a87f15e6f Merge pull request #1117 from sigmavirus24/issue1106
Fix #1106
2013-01-22 18:48:31 -08:00
Kenneth Reitz
472a5b4748 Merge pull request #1127 from sigmavirus24/fix1126
A simple fix
2013-01-22 17:53:16 -08:00
Kenneth Reitz
b94510fc42 Merge pull request #1128 from mvid/master
Updating urllib3 to current master
2013-01-22 17:45:00 -08:00
Mantas Vidutis
6e53290acd update urllib3 to current master with ssl bugfixes 2013-01-22 14:40:10 -08:00
Ian Cordasco
ca85b4ec6c Tests pass this time. 2013-01-22 17:22:12 -05:00
Juan Riaza
a6360ca134 missing algorithm field 2013-01-22 16:35:16 +01:00
Kenneth Reitz
47aa968f3c Merge pull request #1121 from Lukasa/typo
Small typo fixes.
2013-01-22 06:40:24 -08:00
Kenneth Reitz
3bf46609c8 Merge pull request #1097 from matthewlmcclure/issues/1096
Resolves the parts of #1096 in requests proper.
2013-01-22 05:11:14 -08:00
Kenneth Reitz
cd0cbada65 Merge pull request #1095 from Jud/patch-1
Fix spacing in setup.py
2013-01-22 05:10:19 -08:00
Kenneth Reitz
ed559bb8b1 Merge pull request #1122 from Lukasa/readline
Decrease default line length for iter_lines
2013-01-22 05:09:11 -08:00
Ian Cordasco
27e814ad76 Fix failing tests. 2013-01-21 20:21:08 -05:00
Andrew Jesaitis
03893d9b7f Fixes repeated 401s when using DigestAuth with a session 2013-01-21 16:34:11 -07:00
Cory Benfield
297aa04bea Decrease default line length for iter_lines 2013-01-21 21:15:04 +00:00
Cory Benfield
c678a7b402 Small typo fixes. 2013-01-21 19:55:08 +00:00
Kenneth Reitz
b7242a1ad1 Merge pull request #1120 from spulec/patch-2
Add py3.3 testing back
2013-01-21 11:27:41 -08:00
Steve Pulec
cf0a5089f5 Add py3.3 testing back 2013-01-21 14:19:29 -05:00
Cory Benfield
de32f1774f Remove test that cannot work on Python3. 2013-01-19 17:14:32 +00:00
Ian Cordasco
e1c4fe21d4 Fix #1106 2013-01-19 11:49:52 -05:00
Cory Benfield
89c8cbbe0c Ensure Content-Length is a string. 2013-01-19 12:07:34 +00:00
Kenneth Reitz
16ba63ccde Merge pull request #1114 from vlaci/master
Keep-alive support for http proxies
2013-01-18 05:40:26 -08:00
László Vaskó
347a52aa5c Fixed proxy requests to pool connections. 2013-01-18 13:41:14 +01:00
Kenneth Reitz
c920ee57b8 Merge pull request #1110 from slingamn/certifi_eol
Remove support for certifi
2013-01-18 04:39:41 -08:00
Shivaram Lingamneni
985a906c6c Remove support for certifi
As per #1105, certifi is being end-of-lifed. Requests will use either
its own vendored bundle, or possibly (when packaged with OS distributions)
an externally packaged bundle, which can be enabled by patching
requests.certs.where().
2013-01-17 20:44:01 -08:00
Kenneth Reitz
f038f68ad8 Merge pull request #1107 from Lukasa/master
Correct broken cookies example
2013-01-17 19:50:55 -08:00
Cory Benfield
d3e6597f73 Update docs with correct cookie behaviour. 2013-01-17 19:28:28 +00:00
Kenneth Reitz
f8d729a17b Merge pull request #1105 from slingamn/turktrust
refresh CA certificates
2013-01-17 02:51:52 -08:00
Shivaram Lingamneni
b66427eab9 remove the TURKTRUST root certificates, as per #1102 2013-01-17 02:02:49 -08:00
David McKeone
35d4a47cb1 Allow passing POST data with a list of 2-tuples. 2013-01-16 11:53:24 -07:00
sprt
a721d590b4 Make hooks work with prepared requests 2013-01-12 21:46:44 +01:00
Matt McClure
628e393b9a Resolves the parts of #1096 in requests proper. 2013-01-11 15:04:47 -05:00
Jud
a950322b07 Fix spacing in setup.py 2013-01-11 11:42:02 -05:00
Kenneth Reitz
34917618d8 +1 2013-01-11 09:34:14 -05:00
Kenneth Reitz
1a7c91f658 Merge remote-tracking branch 'origin/master' 2013-01-10 02:13:06 -05:00
Kenneth Reitz
41687c8fa8 v1.1.0 and docs 2013-01-10 02:13:02 -05:00
Kenneth Reitz
91f6e69cc4 Merge pull request #1092 from jianlius/case-insensitive-update
Make merge_kwargs case-insensitive when looking up keys.
2013-01-09 23:07:45 -08:00
Kenneth Reitz
6ec1a9ca52 Merge pull request #1091 from vinodc/specify_file_content_type
Allow for explicit file content type support
2013-01-09 23:02:47 -08:00
Kenneth Reitz
9d33629b6d don't supply params for directs
Closes #1070
2013-01-10 01:58:46 -05:00
Kenneth Reitz
ef8563ab36 CHUNKED REQUESTS! 2013-01-10 01:58:29 -05:00
Jian Li
a392a87389 Retrieve kwargs.keys() just once. 2013-01-09 22:07:38 -08:00
Kenneth Reitz
4a5b5bc86e test cleanup 2013-01-10 00:40:17 -05:00
Jian Li
68edcd12b1 Make merge_kwargs case-insensitive when looking up keys. 2013-01-09 21:29:24 -08:00
Vinod Chandru
fbc366c1a5 Fixing test to ensure it passes with python 3. 2013-01-09 20:10:54 -08:00
Vinod Chandru
20b10aed1b Allow for third argument in file dict value to support explicit
file content type.
2013-01-09 19:29:28 -08:00
Kenneth Reitz
b4505284f2 Merge pull request #1080 from sigmavirus24/master
Fix #1079
2013-01-07 10:42:30 -08:00
Kenneth Reitz
47fe258b5a Merge pull request #1085 from graingert/patch-1
Update the advanced doc to use the r.json method
2013-01-07 10:42:14 -08:00
Kenneth Reitz
21616a815b Merge pull request #1086 from graingert/patch-2
Update the link headers doc to match the code
2013-01-07 10:41:51 -08:00
Kenneth Reitz
50934e056e Merge pull request #1089 from PaulMcMillan/philosophy
Clarify support for alternative distributions
2013-01-07 10:41:32 -08:00
Paul McMillan
ba1df2cfca Clarify support for alternative distributions 2013-01-07 13:34:51 -05:00
Thomas Grainger
7dc13e5cab Update the link headers doc to match the code 2013-01-04 21:46:14 +00:00
Thomas Grainger
7687746c7c Update the advanced doc to use the r.json method 2013-01-04 21:33:51 +00:00
Ian Cordasco
5264c71d86 Fix #1079
Restore the functionality from here:
https://github.com/kennethreitz/requests/blob/v0.14.2/requests/models.py#L884
2013-01-02 14:25:33 -05:00
Kenneth Reitz
5d87e1aeba Update LICENSE 2012-12-31 17:59:18 -05:00
Kenneth Reitz
c03e893416 Merge pull request #1076 from sigmavirus24/master
Remove (safe|danger)_mode references from docs
2012-12-30 23:49:44 -08:00
Ian Cordasco
fef09a6691 Remove safe_mode/danger_mode from docs 2012-12-30 19:46:04 -05:00
Kenneth Reitz
1d35d439cc Merge pull request #1071 from Lukasa/master
Proxies should have schemes.
2012-12-28 07:41:55 -08:00
Cory Benfield
054e1ab3ee Remove silly print statement. 2012-12-28 15:38:04 +00:00
Cory Benfield
641f4611b2 Make sure proxies have their scheme attached. 2012-12-27 13:37:36 +00:00
Kenneth Reitz
594716abea Merge pull request #1060 from Lukasa/proxy
Provide full URL to proxies.
2012-12-27 03:24:55 -08:00
Cory Benfield
d675abbd3f Remove unneeded import. 2012-12-27 11:22:36 +00:00
Cory Benfield
702f4039bc Use full URL when contacting proxies.
Requests should use the full URL, not the path URL, when forwarding
traffic through proxies.
2012-12-27 11:22:36 +00:00
Cory Benfield
b479a62ec2 PreparedRequests don't carry proxy information.
The proxy info appears to be carried on the session. The only remnants
of proxy handling are this line in __init__().
2012-12-27 11:22:36 +00:00
Kenneth Reitz
81c18c70f5 Merge pull request #1065 from slingamn/certs.release
clean up code support for OS certificate bundles
2012-12-23 02:49:02 -08:00
Shivaram Lingamneni
bbea679ab2 clean up code support for OS certificate bundles 2012-12-23 02:40:18 -08:00
Kenneth Reitz
f1d51aa7eb timeout 2012-12-23 02:45:41 -05:00
61 changed files with 2069 additions and 591 deletions

3
.gitignore vendored
View File

@@ -5,12 +5,15 @@ nosetests.xml
junit-report.xml
pylint.txt
toy.py
tox.ini
violations.pyflakes.txt
cover/
build/
docs/_build
requests.egg-info/
*.pyc
*.swp
*.egg
env/
.workon

View File

@@ -2,10 +2,10 @@ language: python
python:
- 2.6
- 2.7
- 3.2
- 3.3
env: HTTPBIN_URL=http://httpbin.org/
script: make test
script: invoke test
install:
- make test-deps
- pip install -r requirements.txt
notifications:
email: false

View File

@@ -118,3 +118,13 @@ Patches and Suggestions
- Martijn Pieters
- Jonatan Heyman
- David Bonner <dbonner@gmail.com> @rascalking
- Vinod Chandru
- Johnny Goodnow <j.goodnow29@gmail.com>
- Denis Ryzhkov <denisr@denisr.com>
- Wilfred Hughes <me@wilfred.me.uk> @dontYetKnow
- Dmitry Medvinsky <me@dmedvinsky.name>
- Bryce Boe <bbzbryce@gmail.com> @bboe
- Colin Dunklau <colin.dunklau@gmail.com> @cdunklau
- Hugo Osvaldo Barrera <hugo@osvaldobarrera.com.ar> @hobarrera
- Łukasz Langa <lukasz@langa.pl> @llanga
- Dave Shawley <daveshawley@gmail.com>

View File

@@ -3,6 +3,52 @@
History
-------
1.2.1 (2013-05-20)
++++++++++++++++++
- Python 3.3.2 compatibility
- Always percent-encode location headers
- Fix connection adapter matching to be most-specific first
- new argument to the default connection adapter for passing a block argument
- prevent a KeyError when there's no link headers
1.2.0 (2013-03-31)
++++++++++++++++++
- Fixed cookies on sessions and on requests
- Significantly change how hooks are dispatched - hooks now receive all the
arguments specified by the user when making a request so hooks can make a
secondary request with the same parameters. This is especially necessary for
authentication handler authors
- certifi support was removed
- Fixed bug where using OAuth 1 with body ``signature_type`` sent no data
- Major proxy work thanks to @Lukasa including parsing of proxy authentication
from the proxy url
- Fix DigestAuth handling too many 401s
- Update vendored urllib3 to include SSL bug fixes
- Allow keyword arguments to be passed to ``json.loads()`` via the
``Response.json()`` method
- Don't send ``Content-Length`` header by default on ``GET`` or ``HEAD``
requests
- Add ``elapsed`` attribute to ``Response`` objects to time how long a request
took.
- Fix ``RequestsCookieJar``
- Sessions and Adapters are now picklable, i.e., can be used with the
multiprocessing library
- Update charade to version 1.0.3
The change in how hooks are dispatched will likely cause a great deal of
issues.
1.1.0 (2013-01-10)
++++++++++++++++++
- CHUNKED REQUESTS
- Support for iterable response bodies
- Assume servers persist redirect params
- Allow explicit content types to be specified for file data
- Make merge_kwargs case-insensitive when looking up keys
1.0.3 (2012-12-18)
++++++++++++++++++
@@ -31,7 +77,7 @@ History
- /s/prefetch/stream
- Removal of all configuration
- Standard library logging
- Make Reponse.json() callable, not property.
- Make Response.json() callable, not property.
- Usage of new charade project, which provides python 2 and 3 simultaneous chardet.
- Removal of all hooks except 'response'
- Removal of all authentication helpers (OAuth, Kerberos)
@@ -566,10 +612,10 @@ This is not a backwards compatible change.
++++++++++++++++++
* New HTTPHandling Methods
- Reponse.__nonzero__ (false if bad HTTP Status)
- Response.__nonzero__ (false if bad HTTP Status)
- Response.ok (True if expected HTTP Status)
- Response.error (Logged HTTPError if bad HTTP Status)
- Reponse.raise_for_status() (Raises stored HTTPError)
- Response.raise_for_status() (Raises stored HTTPError)
0.2.2 (2011-02-14)

View File

@@ -1,4 +1,4 @@
Copyright 2012 Kenneth Reitz
Copyright 2013 Kenneth Reitz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@ Copyright 2012 Kenneth Reitz
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.

View File

@@ -1,30 +0,0 @@
SHELL := /bin/bash
test:
py.test
test-deps:
pip install -r requirements.txt
six:
python test_requests.py
python3 test_requests.py
deps: urllib3 certs charade
urllib3:
rm -fr requests/packages/urllib3
git clone https://github.com/shazow/urllib3.git
cd urllib3 && git checkout master && cd ..
mv urllib3/urllib3 requests/packages/
rm -fr urllib3
charade:
rm -fr requests/packages/charade
git clone https://github.com/sigmavirus24/charade.git
cd charade && git checkout master && cd ..
mv charade/charade requests/packages/
rm -fr charade
certs:
cd requests && curl -O https://raw.github.com/kennethreitz/certifi/master/certifi/cacert.pem

2
NOTICE
View File

@@ -6,7 +6,7 @@ Urllib3 License
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt),
Modifications copyright 2022 Kenneth Reitz.
Modifications copyright 2012 Kenneth Reitz.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software

View File

@@ -5,6 +5,9 @@ Requests: HTTP for Humans
.. image:: https://travis-ci.org/kennethreitz/requests.png?branch=master
:target: https://travis-ci.org/kennethreitz/requests
.. image:: https://pypip.in/d/requests/badge.png
:target: https://crate.io/packages/requests/
Requests is an Apache2 Licensed HTTP library, written in Python, for human
beings.
@@ -69,6 +72,11 @@ Or, if you absolutely must:
But, you really shouldn't do that.
Documentation
-------------
Documentation is available at http://docs.python-requests.org/.
Contribute
----------

View File

@@ -1,6 +1,6 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="Rezzy the Requests Sea Turtle"/>
</a>
</p>
@@ -34,13 +34,18 @@
<a href="http://gum.co/RRZc" class="gumroad-button">Requests Pro</a><script type="text/javascript" src="https://gumroad.com/js/gumroad-button.js"></script>
</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>Translations</h3>
<ul>
<li><a href="http://docs.python-requests.org/">English</a></li>
<li><a href="http://fr.python-requests.org/">French</a></li>
<li><a href="http://de.python-requests.org/">German</a></li>
<li><a href="http://jp.python-requests.org/">Japanese</a></li>
<li><a href="http://cn.python-requests.org/">Chinese</a></li>
<li><a href="http://pt.python-requests.org/">Portuguese</a></li>
</ul>
<h3>Useful Links</h3>
<ul>

View File

@@ -1,6 +1,6 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="Rezzy the Requests Sea Turtle"/>
</a>
</p>
<p>
@@ -32,3 +32,4 @@
<p>
<a href="http://gum.co/RRZc" class="gumroad-button">Requests Pro</a><script type="text/javascript" src="https://gumroad.com/js/gumroad-button.js"></script>
</p>

View File

@@ -13,7 +13,7 @@ important right here and provide links to the canonical documentation.
Main Interface
--------------
All of Request's functionality can be accessed by these 7 methods.
All of Requests' functionality can be accessed by these 7 methods.
They all return an instance of the :class:`Response <Response>` object.
.. autofunction:: request
@@ -41,17 +41,18 @@ Request Sessions
.. autoclass:: Session
:inherited-members:
.. autoclass:: requests.adapters.HTTPAdapter
:inherited-members:
Exceptions
~~~~~~~~~~
.. module:: requests
.. autoexception:: RequestException
.. autoexception:: ConnectionError
.. autoexception:: HTTPError
.. autoexception:: URLRequired
.. autoexception:: TooManyRedirects
.. autoexception:: requests.exceptions.RequestException
.. autoexception:: requests.exceptions.ConnectionError
.. autoexception:: requests.exceptions.HTTPError
.. autoexception:: requests.exceptions.URLRequired
.. autoexception:: requests.exceptions.TooManyRedirects
Status Code Lookup
@@ -73,18 +74,17 @@ Status Code Lookup
Cookies
~~~~~~~
.. autofunction:: dict_from_cookiejar
.. autofunction:: cookiejar_from_dict
.. autofunction:: add_dict_to_cookiejar
.. autofunction:: requests.utils.dict_from_cookiejar
.. autofunction:: requests.utils.cookiejar_from_dict
.. autofunction:: requests.utils.add_dict_to_cookiejar
Encodings
~~~~~~~~~
.. autofunction:: get_encodings_from_content
.. autofunction:: get_encoding_from_headers
.. autofunction:: get_unicode_from_response
.. autofunction:: decode_gzip
.. autofunction:: requests.utils.get_encodings_from_content
.. autofunction:: requests.utils.get_encoding_from_headers
.. autofunction:: requests.utils.get_unicode_from_response
Classes
@@ -104,3 +104,91 @@ Classes
.. autoclass:: requests.Session
:inherited-members:
.. autoclass:: requests.adapters.HTTPAdapter
:inherited-members:
Migrating to 1.x
----------------
This section details the main differences between 0.x and 1.x and is meant
to ease the pain of upgrading.
API Changes
~~~~~~~~~~~
* ``Response.json`` is now a callable and not a property of a response.
::
import requests
r = requests.get('https://github.com/timeline.json')
r.json() # This *call* raises an exception if JSON decoding fails
* The ``Session`` API has changed. Sessions objects no longer take parameters.
``Session`` is also now capitalized, but it can still be
instantiated with a lowercase ``session`` for backwards compatibility.
::
s = requests.Session() # formerly, session took parameters
s.auth = auth
s.headers.update(headers)
r = s.get('http://httpbin.org/headers')
* All request hooks have been removed except 'response'.
* Authentication helpers have been broken out into separate modules. See
requests-oauthlib_ and requests-kerberos_.
.. _requests-oauthlib: https://github.com/requests/requests-oauthlib
.. _requests-kerberos: https://github.com/requests/requests-kerberos
* The parameter for streaming requests was changed from ``prefetch`` to
``stream`` and the logic was inverted. In addition, ``stream`` is now
required for raw response reading.
::
# in 0.x, passing prefetch=False would accomplish the same thing
r = requests.get('https://github.com/timeline.json', stream=True)
r.raw.read(10)
* The ``config`` parameter to the requests method has been removed. Some of
these options are now configured on a ``Session`` such as keep-alive and
maximum number of redirects. The verbosity option should be handled by
configuring logging.
::
import requests
import logging
# these two lines enable debugging at httplib level (requests->urllib3->httplib)
# you will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
# the only thing missing will be the response.body which is not logged.
import httplib
httplib.HTTPConnection.debuglevel = 1
logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
requests.get('http://httpbin.org/headers')
Licensing
~~~~~~~~~
One key difference that has nothing to do with the API is a change in the
license from the ISC_ license to the `Apache 2.0`_ license. The Apache 2.0
license ensures that contributions to requests are also covered by the Apache
2.0 license.
.. _ISC: http://opensource.org/licenses/ISC
.. _Apache 2.0: http://opensource.org/licenses/Apache-2.0

View File

@@ -3,7 +3,7 @@
Support
=======
If you have a questions or issues about Requests, there are several options:
If you have questions or issues about Requests, there are several options:
Send a Tweet
------------

View File

@@ -43,7 +43,7 @@ master_doc = 'index'
# General information about the project.
project = u'Requests'
copyright = u'2012. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
copyright = u'2013. 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
@@ -133,7 +133,6 @@ html_static_path = ['_static']
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],

View File

@@ -38,4 +38,4 @@ Linux Distro Packages
Distributions have been made for many Linux repositories, including: Ubuntu, Debian, RHEL, and Arch.
These distributions are often aggressive forks. They are discouraged and not supported.
These distributions are sometimes divergent forks, or are otherwise not kept up-to-date with the latest code and bugfixes. PyPI (and its mirrors) and GitHub are the official distribution sources; alternatives are not supported by the requests project.

View File

@@ -3,22 +3,28 @@ How to Help
Requests is under active development, and contributions are more than welcome!
#. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
#. Check for open issues or open a fresh issue to start a discussion around a bug.
There is a Contributor Friendly tag for issues that should be ideal for people who are not very
familiar with the codebase yet.
#. Fork `the repository <https://github.com/kennethreitz/requests>`_ on Github andstart making your
#. Fork `the repository <https://github.com/kennethreitz/requests>`_ on Github and start making your
changes to a new branch.
#. Write a test which shows that the bug was fixed or that the feature works as expected.
#. Write a test which shows that the bug was fixed.
#. Send a pull request and bug the maintainer until it gets merged and published. :)
Make sure to add yourself to `AUTHORS <https://github.com/kennethreitz/requests/blob/master/AUTHORS.rst>`_.
Feature Freeze
--------------
As of v1.0.0, Requests has now entered a feature freeze. Requests for new
features and Pull Requests implementing those features will not be accepted.
Development Dependencies
------------------------
You'll need to install py.test in order to run the Requests' test suite::
$ make test-deps
$ make test
$ pip install -r requirements.txt
$ invoke test
py.test
platform darwin -- Python 2.7.3 -- pytest-2.3.4
collected 25 items
@@ -40,7 +46,7 @@ Requests currently supports the following versions of Python:
Support for Python 3.1 and 3.2 may be dropped at any time.
Google App Engine will never be officially supported. Pull requests for compatiblity will be accepted, as long as they don't complicate the codebase.
Google App Engine will never be officially supported. Pull requests for compatibility will be accepted, as long as they don't complicate the codebase.
Are you crazy?

View File

@@ -38,7 +38,7 @@ Requests takes all of the work out of Python HTTP/1.1 — making your integrati
Testimonials
------------
Amazon, Google, Twilio, Mozilla, Heroku, PayPal, NPR, Obama for America, Transifex, Native Instruments, The Washington Post, Twitter, SoundCloud, Kippt, Readability, and Federal US Institutions use Requests internally. It has been downloaded over 1,500,000 times from PyPI.
Her Majesty's Government, Amazon, Google, Twilio, Mozilla, Heroku, PayPal, NPR, Obama for America, Transifex, Native Instruments, The Washington Post, Twitter, SoundCloud, Kippt, Readability, and Federal US Institutions use Requests internally. It has been downloaded over 3,000,000 times from PyPI.
**Armin Ronacher**
Requests is the perfect example how beautiful an API can be with the

View File

@@ -75,8 +75,42 @@ request, and then the request's headers::
>>> r.request.headers
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/0.13.1'}
'Accept': '*/*', 'User-Agent': 'python-requests/1.2.0'}
Prepared Requests
-----------------
Whenever you receive a :class:`Response <requests.models.Response>` object
from an API call or a Session call, the ``request`` attribute is actually the
``PreparedRequest`` that was used. In some cases you may wish to do some extra
work to the body or headers (or anything else really) before sending a
request. The simple recipe for this is the following::
from requests import Request, Session
s = Session()
prepped = Request('GET', # or any other method, 'POST', 'PUT', etc.
url,
data=data
headers=headers
# ...
).prepare()
# do something with prepped.body
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout,
# etc.
)
print(resp.status_code)
Since you are not doing anything special with the ``Request`` object, you
prepare it immediately and modified the ``PreparedRequest`` object. You then
send that with the other parameters you would have sent to ``requests.*`` or
``Sesssion.*``.
SSL Cert Verification
---------------------
@@ -93,7 +127,7 @@ I don't have SSL setup on this domain, so it fails. Excellent. Github does thoug
You can also pass ``verify`` the path to a CA_BUNDLE file for private certs. You can also set the ``REQUESTS_CA_BUNDLE`` environment variable.
Requests can also ignore verifying the SSL certficate if you set ``verify`` to False.
Requests can also ignore verifying the SSL certificate if you set ``verify`` to False.
::
@@ -102,7 +136,7 @@ Requests can also ignore verifying the SSL certficate if you set ``verify`` to F
By default, ``verify`` is set to True. Option ``verify`` only applies to host certs.
You can also specify the local cert file either as a path or key value pair::
You can also specify a local cert to use as client side certificate, as a single file (containing the private key and the certificate) or as a tuple of both file's path::
>>> requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))
<Response [200]>
@@ -138,6 +172,28 @@ Excellent news — thanks to urllib3, keep-alive is 100% automatic within a ses
Note that connections are only released back to the pool for reuse once all body data has been read; be sure to either set ``stream`` to ``False`` or read the ``content`` property of the ``Response`` object.
Streaming Uploads
-----------------
Requests supports streaming uploads, which allow you to send large streams or files without reading them into memory. To stream and upload, simply provide a file-like object for your body::
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)
Chunk-Encoded Requests
----------------------
Requests also supports Chunked transfer encoding for outgoing and incoming requests. To send a chunk-encoded request, simply provide a generator (or any iterator without a length) for your body::
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
Event Hooks
-----------
@@ -220,14 +276,15 @@ APIs such as the `Twitter Streaming API <https://dev.twitter.com/docs/streaming-
To use the Twitter Streaming API to track the keyword "requests"::
import requests
import json
import requests
r = requests.post('https://stream.twitter.com/1/statuses/filter.json',
data={'track': 'requests'}, auth=('username', 'password'), stream=True)
r = requests.post('http://httpbin.org/stream/20', stream=True)
for line in r.iter_lines():
if line: # filter out keep-alive new lines
# filter out keep-alive new lines
if line:
print json.loads(line)
@@ -240,8 +297,8 @@ If you need to use a proxy, you can configure individual requests with the
import requests
proxies = {
"http": "10.10.1.10:3128",
"https": "10.10.1.10:1080",
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
@@ -250,8 +307,8 @@ You can also configure proxies by environment variables ``HTTP_PROXY`` and ``HTT
::
$ export HTTP_PROXY="10.10.1.10:3128"
$ export HTTPS_PROXY="10.10.1.10:1080"
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get("http://example.org")
@@ -313,17 +370,12 @@ out what type of content it is. Do this like so::
...
application/json; charset=utf-8
So, GitHub returns JSON. That's great, we can use the JSON module to turn it
into Python objects. Because GitHub returned UTF-8, we should use the
``r.text`` method, not the ``r.content`` method. ``r.content`` returns a
bytestring, while ``r.text`` returns a Unicode-encoded string. I have no plans
to perform byte-manipulation on this response, so I want any Unicode code
points encoded.
So, GitHub returns JSON. That's great, we can use the ``r.json`` method to
parse it into Python objects.
::
>>> import json
>>> commit_data = json.loads(r.text)
>>> commit_data = r.json()
>>> print commit_data.keys()
[u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message']
>>> print commit_data[u'committer']
@@ -380,7 +432,7 @@ Cool, we have three comments. Let's take a look at the last of them.
>>> r = requests.get(r.url + u'/comments')
>>> r.status_code
200
>>> comments = json.loads(r.text)
>>> comments = r.json()
>>> print comments[0].keys()
[u'body', u'url', u'created_at', u'updated_at', u'user', u'id']
>>> print comments[2][u'body']
@@ -417,7 +469,7 @@ the very common Basic Auth.
>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201
>>> content = json.loads(r.text)
>>> content = r.json()
>>> print content[u'body']
Sounds great! I'll get right on it.
@@ -480,9 +532,45 @@ GitHub uses these for `pagination <http://developer.github.com/v3/#pagination>`_
Requests will automatically parse these link headers and make them easily consumable::
>>> r.links['next']
'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10'
>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links['last']
'https://api.github.com/users/kennethreitz/repos?page=6&per_page=10'
>>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
Transport Adapters
------------------
As of v1.0.0, Requests has moved to a modular internal design. Part of the
reason this was done was to implement Transport Adapters, originally
`described here`_. Transport Adapters provide a mechanism to define interaction
methods for an HTTP service. In particular, they allow you to apply per-service
configuration.
Requests ships with a single Transport Adapter, the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. This adapter provides the
default Requests interaction with HTTP and HTTPS using the powerful `urllib3`_
library. Whenever a Requests :class:`Session <Session>` is initialized, one of
these is attached to the :class:`Session <Session>` object for HTTP, and one
for HTTPS.
Requests enables users to create and use their own Transport Adapters that
provide specific functionality. Once created, a Transport Adapter can be
mounted to a Session object, along with an indication of which web services
it should apply to.
::
>>> s = requests.Session()
>>> s.mount('http://www.github.com', MyAdapter())
The mount call registers a specific instance of a Transport Adapter to a
prefix. Once mounted, any HTTP request made using that session whose URL starts
with the given prefix will use the given Transport Adapter.
Implementing a Transport Adapter is beyond the scope of this documentation, but
a good start would be to subclass the ``requests.adapters.BaseAdapter`` class.
.. _`described here`: http://kennethreitz.org/exposures/the-future-of-python-http
.. _`urllib3`: https://github.com/shazow/urllib3

View File

@@ -44,6 +44,25 @@ and Requests supports this out of the box as well::
<Response [200]>
OAuth 1 Authentication
----------------------
A common form of authentication for several web APIs is OAuth. The ``requests-oauthlib`` library allows Requests users to easily make OAuth authenticated requests::
>>> import requests
>>> from requests_oauthlib import OAuth1
>>> url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
>>> auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
>>> requests.get(url, auth=auth)
<Response [200]>
For more information on how to OAuth flow works, please see the official `OAuth`_ website.
For examples and documentation on requests-oauthlib, please see the `requests_oauthlib`_ repository on GitHub
Other Authentication
--------------------
@@ -53,7 +72,6 @@ authentication handlers for more complicated or less commonly-used forms of
authentication. Some of the best have been brought together under the
`Requests organization`_, including:
- OAuth_
- Kerberos_
- NTLM_
@@ -77,7 +95,8 @@ authentication will additionally add hooks to provide further functionality.
Examples can be found under the `Requests organization`_ and in the
``auth.py`` file.
.. _OAuth: https://github.com/requests/requests-oauthlib
.. _OAuth: http://oauth.net/
.. _requests_oauthlib: https://github.com/requests/requests-oauthlib
.. _Kerberos: https://github.com/requests/requests-kerberos
.. _NTLM: https://github.com/requests/requests-ntlm
.. _Requests organization: https://github.com/requests

View File

@@ -131,7 +131,9 @@ There's also a builtin JSON decoder, in case you're dealing with JSON data::
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
In case the JSON decoding fails, ``r.json`` raises an exception.
In case the JSON decoding fails, ``r.json`` raises an exception. For example, if
the response gets a 401 (Unauthorized), attempting ``r.json`` raises ``ValueError:
No JSON object could be decoded``
Raw Response Content
@@ -141,7 +143,7 @@ In the rare case that you'd like to get the raw socket response from the
server, you can access ``r.raw``. If you want to do this, make sure you set
``stream=True`` in your initial request. Once you do, you can do this::
>>> r = requests.get('https:/github.com/timeline.json', stream=True)
>>> r = requests.get('https://github.com/timeline.json', stream=True)
>>> r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
@@ -287,7 +289,6 @@ We can view the server's response headers using a Python dictionary::
>>> r.headers
{
'status': '200 OK',
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
@@ -320,11 +321,11 @@ Cookies
If a response contains some Cookies, you can get quick access to them::
>>> url = 'http://httpbin.org/cookies/set/requests-is/awesome'
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['requests-is']
'awesome'
>>> r.cookies['example_cookie_name']
'example_cookie_value'
To send your own cookies to the server, you can use the ``cookies``
parameter::
@@ -385,7 +386,8 @@ seconds with the ``timeout`` parameter::
>>> requests.get('http://github.com', timeout=0.001)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: Request timed out.
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
.. admonition:: Note:
@@ -410,12 +412,6 @@ If a request exceeds the configured number of maximum redirections, a
All exceptions that Requests explicitly raises inherit from
:class:`requests.exceptions.RequestException`.
You can refer to :ref:`Configuration API Docs <configurations>` for immediate
raising of :class:`HTTPError` exceptions via the ``danger_mode`` option or
have Requests catch the majority of
:class:`requests.exceptions.RequestException` exceptions with the ``safe_mode``
option.
-----------------------
Ready for more? Check out the :ref:`advanced <advanced>` section.

5
invokefile.py Normal file
View File

@@ -0,0 +1,5 @@
from invoke import run, task
@task
def build():
print("Building!")

View File

@@ -36,18 +36,24 @@ usage:
The other HTTP methods are supported - see `requests.api`. Full documentation
is at <http://python-requests.org>.
:copyright: (c) 2012 by Kenneth Reitz.
:copyright: (c) 2013 by Kenneth Reitz.
:license: Apache 2.0, see LICENSE for more details.
"""
__title__ = 'requests'
__version__ = '1.0.4'
__build__ = 0x01004
__version__ = '1.2.1'
__build__ = 0x010201
__author__ = 'Kenneth Reitz'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2012 Kenneth Reitz'
__copyright__ = 'Copyright 2013 Kenneth Reitz'
# Attempt to enable urllib3's SNI support, if possible
try:
from requests.packages.urllib3.contrib import pyopenssl
pyopenssl.inject_into_urllib3()
except ImportError:
pass
from . import utils
from .models import Request, Response, PreparedRequest

View File

@@ -11,10 +11,11 @@ and maintain connections.
import socket
from .models import Response
from .packages.urllib3.poolmanager import PoolManager, proxy_from_url
from .hooks import dispatch_hook
from .compat import urlparse, basestring
from .utils import DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers
from .packages.urllib3.poolmanager import PoolManager, ProxyManager
from .packages.urllib3.response import HTTPResponse
from .compat import urlparse, basestring, urldefrag, unquote
from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers,
prepend_scheme_if_needed, get_auth_from_url)
from .structures import CaseInsensitiveDict
from .packages.urllib3.exceptions import MaxRetryError
from .packages.urllib3.exceptions import TimeoutError
@@ -22,6 +23,7 @@ from .packages.urllib3.exceptions import SSLError as _SSLError
from .packages.urllib3.exceptions import HTTPError as _HTTPError
from .cookies import extract_cookies_to_jar
from .exceptions import ConnectionError, Timeout, SSLError
from .auth import _basic_auth_str
DEFAULT_POOLSIZE = 10
DEFAULT_RETRIES = 0
@@ -41,19 +43,72 @@ class BaseAdapter(object):
class HTTPAdapter(BaseAdapter):
"""Built-In HTTP Adapter for Urllib3."""
def __init__(self, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE):
self.max_retries = DEFAULT_RETRIES
"""The built-in HTTP Adapter for urllib3.
Provides a general-case interface for Requests sessions to contact HTTP and
HTTPS urls by implementing the Transport Adapter interface. This class will
usually be created by the :class:`Session <Session>` class under the
covers.
:param pool_connections: The number of urllib3 connection pools to cache.
:param pool_maxsize: The maximum number of connections to save in the pool.
:param max_retries: The maximum number of retries each connection should attempt.
Usage::
>>> import requests
>>> s = requests.Session()
>>> a = requests.adapters.HTTPAdapter()
>>> s.mount('http://', a)
"""
__attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize']
def __init__(self, pool_connections=DEFAULT_POOLSIZE,
pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES):
self.max_retries = max_retries
self.config = {}
super(HTTPAdapter, self).__init__()
self._pool_connections = pool_connections
self._pool_maxsize = pool_maxsize
self.init_poolmanager(pool_connections, pool_maxsize)
def __getstate__(self):
return dict((attr, getattr(self, attr, None)) for attr in
self.__attrs__)
def __setstate__(self, state):
for attr, value in state.items():
setattr(self, attr, value)
self.init_poolmanager(self._pool_connections, self._pool_maxsize)
def init_poolmanager(self, connections, maxsize):
"""Initializes a urllib3 PoolManager. This method should not be called
from user code, and is only exposed for use when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
:param connections: The number of urllib3 connection pools to cache.
:param maxsize: The maximum number of connections to save in the pool.
"""
# save these values for pickling
self._pool_connections = connections
self._pool_maxsize = maxsize
self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize)
def cert_verify(self, conn, url, verify, cert):
"""Verify a SSL certificate. This method should not be called from user
code, and is only exposed for use when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
:param conn: The urllib3 connection object associated with the cert.
:param url: The requested URL.
:param verify: Whether we should actually verify the certificate.
:param cert: The SSL certificate to verify.
"""
if url.startswith('https') and verify:
cert_loc = None
@@ -82,6 +137,14 @@ class HTTPAdapter(BaseAdapter):
conn.cert_file = cert
def build_response(self, req, resp):
"""Builds a :class:`Response <requests.Response>` object from a urllib3
response. This should not be called from user code, and is only exposed
for use when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
:param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
:param resp: The urllib3 response object.
"""
response = Response()
# Fallback to None if there's no status_code, for whatever reason.
@@ -93,6 +156,7 @@ class HTTPAdapter(BaseAdapter):
# Set encoding.
response.encoding = get_encoding_from_headers(response.headers)
response.raw = resp
response.reason = response.raw.reason
if isinstance(req.url, bytes):
response.url = req.url.decode('utf-8')
@@ -106,51 +170,146 @@ class HTTPAdapter(BaseAdapter):
response.request = req
response.connection = self
# Run the Response hook.
response = dispatch_hook('response', req.hooks, response)
return response
def get_connection(self, url, proxies=None):
"""Returns a connection for the given URL."""
"""Returns a urllib3 connection for the given URL. This should not be
called from user code, and is only exposed for use when subclassing the
:class:`HTTPAdapter <reqeusts.adapters.HTTPAdapter>`.
:param url: The URL to connect to.
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
"""
proxies = proxies or {}
proxy = proxies.get(urlparse(url).scheme)
if proxy:
conn = proxy_from_url(proxy)
proxy = prepend_scheme_if_needed(proxy, urlparse(url).scheme)
conn = ProxyManager(self.poolmanager.connection_from_url(proxy))
else:
conn = self.poolmanager.connection_from_url(url)
return conn
def close(self):
"""Dispose of any internal state.
"""Disposes of any internal state.
Currently, this just closes the PoolManager, which closes pooled
connections.
"""
self.poolmanager.clear()
def request_url(self, request, proxies):
"""Obtain the url to use when making the final request.
If the message is being sent through a proxy, the full URL has to be
used. Otherwise, we should only use the path portion of the URL.
This shoudl not be called from user code, and is only exposed for use
when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
:param proxies: A dictionary of schemes to proxy URLs.
"""
proxies = proxies or {}
proxy = proxies.get(urlparse(request.url).scheme)
if proxy:
url, _ = urldefrag(request.url)
else:
url = request.path_url
return url
def add_headers(self, request, **kwargs):
"""Add any headers needed by the connection. Currently this adds a
Proxy-Authorization header.
This should not be called from user code, and is only exposed for use
when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
:param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
:param kwargs: The keyword arguments from the call to send().
"""
proxies = kwargs.get('proxies', {})
if proxies is None:
proxies = {}
proxy = proxies.get(urlparse(request.url).scheme)
username, password = get_auth_from_url(proxy)
if username and password:
# Proxy auth usernames and passwords will be urlencoded, we need
# to decode them.
username = unquote(username)
password = unquote(password)
request.headers['Proxy-Authorization'] = _basic_auth_str(username,
password)
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
"""Sends PreparedRequest object. Returns Response object."""
"""Sends PreparedRequest object. Returns Response object.
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
:param stream: (optional) Whether to stream the request content.
:param timeout: (optional) The timeout on the request.
:param verify: (optional) Whether to verify SSL certificates.
:param vert: (optional) Any user-provided SSL certificate to be trusted.
:param proxies: (optional) The proxies dictionary to apply to the request.
"""
conn = self.get_connection(request.url, proxies)
self.cert_verify(conn, request.url, verify, cert)
url = self.request_url(request, proxies)
self.add_headers(request, proxies=proxies)
chunked = not (request.body is None or 'Content-Length' in request.headers)
try:
if not chunked:
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body,
headers=request.headers,
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,
retries=self.max_retries,
timeout=timeout
)
# Send the request.
resp = conn.urlopen(
method=request.method,
url=request.path_url,
body=request.body,
headers=request.headers,
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,
retries=self.max_retries,
timeout=timeout,
)
else:
if hasattr(conn, 'proxy_pool'):
conn = conn.proxy_pool
low_conn = conn._get_conn(timeout=timeout)
low_conn.putrequest(request.method, url, skip_accept_encoding=True)
for header, value in request.headers.items():
low_conn.putheader(header, value)
low_conn.endheaders()
for i in request.body:
low_conn.send(hex(len(i))[2:].encode('utf-8'))
low_conn.send(b'\r\n')
low_conn.send(i)
low_conn.send(b'\r\n')
low_conn.send(b'0\r\n\r\n')
r = low_conn.getresponse()
resp = HTTPResponse.from_httplib(r,
pool=conn,
connection=low_conn,
preload_content=False,
decode_content=False
)
except socket.error as sockerr:
raise ConnectionError(sockerr)
@@ -164,7 +323,7 @@ class HTTPAdapter(BaseAdapter):
elif isinstance(e, TimeoutError):
raise Timeout(e)
else:
raise Timeout('Request timed out.')
raise
r = self.build_response(request, resp)

View File

@@ -73,6 +73,7 @@ def head(url, **kwargs):
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', False)
return request('head', url, **kwargs)

View File

@@ -8,6 +8,7 @@ This module contains the authentication handlers for Requests.
"""
import os
import re
import time
import hashlib
import logging
@@ -36,6 +37,7 @@ class AuthBase(object):
def __call__(self, r):
raise NotImplementedError('Auth hooks must be callable.')
class HTTPBasicAuth(AuthBase):
"""Attaches HTTP Basic Authentication to the given Request object."""
def __init__(self, username, password):
@@ -48,7 +50,7 @@ class HTTPBasicAuth(AuthBase):
class HTTPProxyAuth(HTTPBasicAuth):
"""Attaches HTTP Proxy Authenetication to a given Request object."""
"""Attaches HTTP Proxy Authentication to a given Request object."""
def __call__(self, r):
r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)
return r
@@ -68,18 +70,21 @@ class HTTPDigestAuth(AuthBase):
realm = self.chal['realm']
nonce = self.chal['nonce']
qop = self.chal.get('qop')
algorithm = self.chal.get('algorithm', 'MD5')
opaque = self.chal.get('opaque', None)
algorithm = self.chal.get('algorithm')
opaque = self.chal.get('opaque')
algorithm = algorithm.upper()
if algorithm is None:
_algorithm = 'MD5'
else:
_algorithm = algorithm.upper()
# lambdas assume digest modules are imported at the top level
if algorithm == 'MD5':
if _algorithm == 'MD5':
def md5_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.md5(x).hexdigest()
hash_utf8 = md5_utf8
elif algorithm == 'SHA':
elif _algorithm == 'SHA':
def sha_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
@@ -126,26 +131,29 @@ class HTTPDigestAuth(AuthBase):
# XXX should the partial digests be encoded too?
base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
'response="%s"' % (self.username, realm, nonce, path, respdig)
'response="%s"' % (self.username, realm, nonce, path, respdig)
if opaque:
base += ', opaque="%s"' % opaque
if algorithm:
base += ', algorithm="%s"' % algorithm
if entdig:
base += ', digest="%s"' % entdig
base += ', algorithm="%s"' % algorithm
if qop:
base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
return 'Digest %s' % (base)
def handle_401(self, r):
def handle_401(self, r, **kwargs):
"""Takes the given response and tries digest-auth, if needed."""
num_401_calls = r.request.hooks['response'].count(self.handle_401)
num_401_calls = getattr(self, 'num_401_calls', 1)
s_auth = r.headers.get('www-authenticate', '')
if 'digest' in s_auth.lower() and num_401_calls < 2:
self.chal = parse_dict_header(s_auth.replace('Digest ', ''))
setattr(self, 'num_401_calls', num_401_calls + 1)
pat = re.compile(r'digest ', flags=re.IGNORECASE)
self.chal = parse_dict_header(pat.sub('', s_auth))
# Consume content and release the original connection
# to allow our new request to reuse the same one.
@@ -153,11 +161,12 @@ class HTTPDigestAuth(AuthBase):
r.raw.release_conn()
r.request.headers['Authorization'] = self.build_digest_header(r.request.method, r.request.url)
_r = r.connection.send(r.request)
_r = r.connection.send(r.request, **kwargs)
_r.history.append(r)
return _r
setattr(self, 'num_401_calls', 1)
return r
def __call__(self, r):

View File

@@ -1603,54 +1603,6 @@ vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
oKfN5XozNmr6mis=
-----END CERTIFICATE-----
TURKTRUST Certificate Services Provider Root 1
==============================================
-----BEGIN CERTIFICATE-----
MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP
MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0
acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx
MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg
U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB
TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC
aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX
yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i
Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ
8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4
W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME
BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46
sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE
q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY
nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H
-----END CERTIFICATE-----
TURKTRUST Certificate Services Provider Root 2
==============================================
-----BEGIN CERTIFICATE-----
MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN
MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr
dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G
A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe
LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI
x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g
QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr
5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB
AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt
Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+
hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P
9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5
UrbnBEI=
-----END CERTIFICATE-----
SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----

View File

@@ -2,26 +2,23 @@
# -*- coding: utf-8 -*-
"""
ceritfi.py
~~~~~~~~~~
certs.py
~~~~~~~~
This module returns the installation location of cacert.pem.
This module returns the preferred default CA certificate bundle.
If you are packaging Requests, e.g., for a Linux distribution or a managed
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""
import os
try:
import certifi
except ImportError:
certifi = None
import os.path
def where():
if certifi:
return certifi.where()
else:
f = os.path.split(__file__)[0]
return os.path.join(f, 'cacert.pem')
"""Return the preferred certificate bundle."""
# vendored bundle inside Requests
return os.path.join(os.path.dirname(__file__), 'cacert.pem')
if __name__ == '__main__':
print(where())

View File

@@ -98,7 +98,6 @@ if is_py2:
numeric_types = (int, long, float)
elif is_py3:
from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag
from urllib.request import parse_http_list

View File

@@ -69,6 +69,10 @@ class MockRequest(object):
def unverifiable(self):
return self.is_unverifiable()
@property
def origin_req_host(self):
return self.get_origin_req_host()
class MockResponse(object):
"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
@@ -240,18 +244,28 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping):
"""Dict-like __getitem__() for compatibility with client code. Throws exception
if there are more than one cookie with name. In that case, use the more
explicit get() method instead. Caution: operation is O(n), not O(1)."""
return self._find_no_duplicates(name)
def __setitem__(self, name, value):
"""Dict-like __setitem__ for compatibility with client code. Throws exception
if there is already a cookie of that name in the jar. In that case, use the more
explicit set() method instead."""
self.set(name, value)
def __delitem__(self, name):
"""Deletes a cookie given a name. Wraps cookielib.CookieJar's remove_cookie_by_name()."""
remove_cookie_by_name(self, name)
def update(self, other):
"""Updates this jar with cookies from another CookieJar or dict-like"""
if isinstance(other, cookielib.CookieJar):
for cookie in other:
self.set_cookie(cookie)
else:
super(RequestsCookieJar, self).update(other)
def _find(self, name, domain=None, path=None):
"""Requests uses this method internally to get cookie values. Takes as args name
and optional domain and path. Returns a cookie.value. If there are conflicting cookies,
@@ -297,8 +311,10 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping):
self._cookies_lock = threading.RLock()
def copy(self):
"""This is not implemented. Calling this will throw an exception."""
raise NotImplementedError
"""Return a copy of this RequestsCookieJar."""
new_cj = RequestsCookieJar()
new_cj.update(self)
return new_cj
def create_cookie(name, value, **kwargs):

View File

@@ -16,7 +16,11 @@ class RequestException(RuntimeError):
class HTTPError(RequestException):
"""An HTTP error occurred."""
response = None
def __init__(self, *args, **kwargs):
""" Initializes HTTPError with optional `response` object. """
self.response = kwargs.pop('response', None)
super(HTTPError, self).__init__(*args, **kwargs)
class ConnectionError(RequestException):

View File

@@ -16,6 +16,7 @@ Available hooks:
HOOKS = ['response']
def default_hooks():
hooks = {}
for event in HOOKS:
@@ -24,7 +25,8 @@ def default_hooks():
# TODO: response is the only one
def dispatch_hook(key, hooks, hook_data):
def dispatch_hook(key, hooks, hook_data, **kwargs):
"""Dispatches a hook dictionary on a given piece of data."""
hooks = hooks or dict()
@@ -36,7 +38,7 @@ def dispatch_hook(key, hooks, hook_data):
hooks = [hooks]
for hook in hooks:
_hook_data = hook(hook_data)
_hook_data = hook(hook_data, **kwargs)
if _hook_data is not None:
hook_data = _hook_data

View File

@@ -9,27 +9,27 @@ This module contains the primary objects that power Requests.
import collections
import logging
import datetime
from io import BytesIO
from .hooks import default_hooks
from .structures import CaseInsensitiveDict
from .status_codes import codes
from .auth import HTTPBasicAuth
from .cookies import cookiejar_from_dict, get_cookie_header
from .packages.urllib3.filepost import encode_multipart_formdata
from .packages.urllib3.util import parse_url
from .exceptions import HTTPError, RequestException, MissingSchema, InvalidURL
from .utils import (
stream_untransfer, guess_filename, requote_uri,
guess_filename, get_auth_from_url, requote_uri,
stream_decode_response_unicode, to_key_val_list, parse_header_links,
iter_slices, guess_json_utf)
iter_slices, guess_json_utf, super_len)
from .compat import (
cookielib, urlparse, urlunparse, urlsplit, urlencode, str, bytes, StringIO,
is_py2, chardet, json, builtin_str, basestring)
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
CONTENT_CHUNK_SIZE = 10 * 1024
ITER_CHUNK_SIZE = 10 * 1024
ITER_CHUNK_SIZE = 512
log = logging.getLogger(__name__)
@@ -61,7 +61,7 @@ class RequestEncodingMixin(object):
"""Encode parameters in a piece of data.
Will successfully encode parameters when passed as a dict or a list of
2-tuples. Order is retained if data is a list of 2-tuples but abritrary
2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
if parameters are supplied as a dict.
"""
@@ -100,16 +100,22 @@ class RequestEncodingMixin(object):
files = to_key_val_list(files or {})
for field, val in fields:
if isinstance(val, list):
for v in val:
new_fields.append((field, builtin_str(v)))
else:
new_fields.append((field, builtin_str(val)))
if isinstance(val, basestring) or not hasattr(val, '__iter__'):
val = [val]
for v in val:
if v is not None:
new_fields.append(
(field.encode('utf-8') if isinstance(field, str) else field,
v.encode('utf-8') if isinstance(v, str) else v))
for (k, v) in files:
# support for explicit filename
ft = None
if isinstance(v, (tuple, list)):
fn, fp = v
if len(v) == 2:
fn, fp = v
else:
fn, fp, ft = v
else:
fn = guess_filename(v) or k
fp = v
@@ -117,7 +123,12 @@ class RequestEncodingMixin(object):
fp = StringIO(fp)
if isinstance(fp, bytes):
fp = BytesIO(fp)
new_fields.append((k, (fn, fp.read())))
if ft:
new_v = (fn, fp.read(), ft)
else:
new_v = (fn, fp.read())
new_fields.append((k, new_v))
body, content_type = encode_multipart_formdata(new_fields)
@@ -179,7 +190,6 @@ class Request(RequestHooksMixin):
cookies=None,
hooks=None):
# Default empty dicts for dict params.
data = [] if data is None else data
files = [] if files is None else files
@@ -213,9 +223,12 @@ class Request(RequestHooksMixin):
p.prepare_headers(self.headers)
p.prepare_cookies(self.cookies)
p.prepare_body(self.data, self.files)
p.prepare_auth(self.auth, self.url)
# Note that prepare_auth must be last to enable authentication schemes
# such as OAuth to work on a fully prepared request.
p.prepare_auth(self.auth)
# This MUST go after prepare_auth. Authenticators could add a hook
p.prepare_hooks(self.hooks)
return p
@@ -272,16 +285,28 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
pass
# Support for unicode domain names and paths.
scheme, netloc, path, _params, query, fragment = urlparse(url)
scheme, auth, host, port, path, query, fragment = parse_url(url)
if not scheme:
raise MissingSchema("Invalid URL %r: No schema supplied" % url)
if not host:
raise InvalidURL("Invalid URL %t: No host supplied" % url)
# Only want to apply IDNA to the hostname
try:
netloc = netloc.encode('idna').decode('utf-8')
host = host.encode('idna').decode('utf-8')
except UnicodeError:
raise InvalidURL('URL has an invalid label.')
# Carefully reconstruct the network location
netloc = auth or ''
if netloc:
netloc += '@'
netloc += host
if port:
netloc += ':' + str(port)
# Bare domains aren't valid URLs.
if not path:
path = '/'
@@ -293,8 +318,6 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
netloc = netloc.encode('utf-8')
if isinstance(path, str):
path = path.encode('utf-8')
if isinstance(_params, str):
_params = _params.encode('utf-8')
if isinstance(query, str):
query = query.encode('utf-8')
if isinstance(fragment, str):
@@ -307,13 +330,14 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
else:
query = enc_params
url = requote_uri(urlunparse([scheme, netloc, path, _params, query, fragment]))
url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
self.url = url
def prepare_headers(self, headers):
"""Prepares the given HTTP headers."""
if headers:
headers = dict((name.encode('ascii'), value) for name, value in headers.items())
self.headers = CaseInsensitiveDict(headers)
else:
self.headers = CaseInsensitiveDict()
@@ -321,42 +345,80 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
def prepare_body(self, data, files):
"""Prepares the given HTTP body data."""
# If a generator is provided, error out.
if isinstance(data, type(_ for _ in [])):
raise NotImplementedError('Generator bodies are not supported yet.')
# Check if file, fo, generator, iterator.
# If not, run through normal process.
# Nottin' on you.
body = None
content_type = None
length = None
is_stream = False
is_stream = all([
hasattr(data, '__iter__'),
not isinstance(data, basestring),
not isinstance(data, list),
not isinstance(data, dict)
])
try:
length = super_len(data)
except (TypeError, AttributeError):
length = False
if is_stream:
body = data
if files:
raise NotImplementedError('Streamed bodies and files are mutually exclusive.')
if length:
self.headers['Content-Length'] = str(length)
else:
self.headers['Transfer-Encoding'] = 'chunked'
# Check if file, fo, generator, iterator.
# If not, run through normal process.
# Multi-part file uploads.
if files:
(body, content_type) = self._encode_files(files, data)
else:
if data:
# Multi-part file uploads.
if files:
(body, content_type) = self._encode_files(files, data)
else:
if data:
body = self._encode_params(data)
if isinstance(data, str) or isinstance(data, builtin_str) or hasattr(data, 'read'):
content_type = None
else:
content_type = 'application/x-www-form-urlencoded'
body = self._encode_params(data)
if isinstance(data, str) or isinstance(data, builtin_str) or hasattr(data, 'read'):
content_type = None
else:
content_type = 'application/x-www-form-urlencoded'
self.prepare_content_length(body)
self.headers['Content-Length'] = '0'
# Add content-type if it wasn't explicitly provided.
if (content_type) and (not 'content-type' in self.headers):
self.headers['Content-Type'] = content_type
self.body = body
def prepare_content_length(self, body):
if hasattr(body, 'seek') and hasattr(body, 'tell'):
body.seek(0, 2)
self.headers['Content-Length'] = str(body.tell())
body.seek(0, 0)
elif body is not None:
self.headers['Content-Length'] = str(len(body))
l = super_len(body)
if l:
self.headers['Content-Length'] = str(l)
elif self.method not in ('GET', 'HEAD'):
self.headers['Content-Length'] = '0'
# Add content-type if it wasn't explicitly provided.
if (content_type) and (not 'content-type' in self.headers):
self.headers['Content-Type'] = content_type
self.body = body
def prepare_auth(self, auth):
def prepare_auth(self, auth, url=''):
"""Prepares the given HTTP auth data."""
# If no Auth is explicitly provided, extract it from the URL first.
if auth is None:
url_auth = get_auth_from_url(self.url)
auth = url_auth if any(url_auth) else None
if auth:
if isinstance(auth, tuple) and len(auth) == 2:
# special-case basic HTTP auth
@@ -368,6 +430,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
# Update self to reflect the auth changes.
self.__dict__.update(r.__dict__)
# Recompute Content-Length
self.prepare_content_length(self.body)
def prepare_cookies(self, cookies):
"""Prepares the given HTTP cookie data."""
@@ -381,6 +446,11 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
if cookie_header is not None:
self.headers['Cookie'] = cookie_header
def prepare_hooks(self, hooks):
"""Prepares the given hooks."""
for event in hooks:
self.register_hook(event, hooks[event])
class Response(object):
"""The :class:`Response <Response>` object, which contains a
@@ -422,6 +492,10 @@ class Response(object):
#: A CookieJar of Cookies the server sent back.
self.cookies = cookiejar_from_dict({})
#: The amount of time elapsed between sending the request
#: and the arrival of the response (as a timedelta)
self.elapsed = datetime.timedelta(0)
def __repr__(self):
return '<Response [%s]>' % (self.status_code)
@@ -433,6 +507,10 @@ class Response(object):
"""Returns true if :attr:`status_code` is 'OK'."""
return self.ok
def __iter__(self):
"""Allows you to use a response as an iterator."""
return self.iter_content(128)
@property
def ok(self):
try:
@@ -448,10 +526,11 @@ class Response(object):
return chardet.detect(self.content)['encoding']
def iter_content(self, chunk_size=1, decode_unicode=False):
"""Iterates over the response data. This avoids reading the content
at once into memory for large responses. The chunk size is the number
of bytes it should read into memory. This is not necessarily the
length of each item returned as decoding can take place.
"""Iterates over the response data. When stream=True is set on the
request, this avoids reading the content at once into memory for
large responses. The chunk size is the number of bytes it should
read into memory. This is not necessarily the length of each item
returned as decoding can take place.
"""
if self._content_consumed:
# simulate reading small chunks of the content
@@ -459,13 +538,13 @@ class Response(object):
def generate():
while 1:
chunk = self.raw.read(chunk_size)
chunk = self.raw.read(chunk_size, decode_content=True)
if not chunk:
break
yield chunk
self._content_consumed = True
gen = stream_untransfer(generate(), self)
gen = generate()
if decode_unicode:
gen = stream_decode_response_unicode(gen, self)
@@ -473,16 +552,15 @@ class Response(object):
return gen
def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None):
"""Iterates over the response data, one line at a time. This
avoids reading the content at once into memory for large
responses.
"""Iterates over the response data, one line at a time. When
stream=True is set on the request, this avoids reading the
content at once into memory for large responses.
"""
pending = None
for chunk in self.iter_content(
chunk_size=chunk_size,
decode_unicode=decode_unicode):
for chunk in self.iter_content(chunk_size=chunk_size,
decode_unicode=decode_unicode):
if pending is not None:
chunk = pending + chunk
@@ -510,7 +588,7 @@ class Response(object):
raise RuntimeError(
'The content for this response was already consumed')
if self.status_code is 0:
if self.status_code == 0:
self._content = None
else:
self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
@@ -556,8 +634,11 @@ class Response(object):
return content
def json(self):
"""Returns the json-encoded content of a response, if any."""
def json(self, **kwargs):
"""Returns the json-encoded content of a response, if any.
:param \*\*kwargs: Optional arguments that ``json.loads`` takes.
"""
if not self.encoding and len(self.content) > 3:
# No encoding set. JSON RFC 4627 section 3 states we should expect
@@ -566,14 +647,14 @@ class Response(object):
# a best guess).
encoding = guess_json_utf(self.content)
if encoding is not None:
return json.loads(self.content.decode(encoding))
return json.loads(self.text or self.content)
return json.loads(self.content.decode(encoding), **kwargs)
return json.loads(self.text or self.content, **kwargs)
@property
def links(self):
"""Returns the parsed header links of the response, if any."""
header = self.headers['link']
header = self.headers.get('link')
# l = MultiDict()
l = {}
@@ -588,7 +669,7 @@ class Response(object):
return l
def raise_for_status(self):
"""Raises stored :class:`HTTPError` or :class:`URLError`, if one occurred."""
"""Raises stored :class:`HTTPError`, if one occurred."""
http_error_msg = ''
@@ -599,9 +680,7 @@ class Response(object):
http_error_msg = '%s Server Error: %s' % (self.status_code, self.reason)
if http_error_msg:
http_error = HTTPError(http_error_msg)
http_error.response = self
raise http_error
raise HTTPError(http_error_msg, response=self)
def close(self):
return self.raw.release_conn()

View File

@@ -15,10 +15,15 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
__version__ = "1.0.1"
__version__ = "1.0.3"
from sys import version_info
def detect(aBuf):
if ((version_info < (3, 0) and isinstance(aBuf, unicode)) or
(version_info >= (3, 0) and not isinstance(aBuf, bytes))):
raise ValueError('Expected a bytes object, not a unicode object')
from . import universaldetector
u = universaldetector.UniversalDetector()
u.reset()

View File

@@ -40,6 +40,7 @@ from .compat import wrap_ord
ENOUGH_DATA_THRESHOLD = 1024
SURE_YES = 0.99
SURE_NO = 0.01
MINIMUM_DATA_THRESHOLD = 3
class CharDistributionAnalysis:
@@ -82,7 +83,7 @@ class CharDistributionAnalysis:
"""return confidence based on existing data"""
# if we didn't receive any character in our consideration range,
# return negative answer
if self._mTotalChars <= 0:
if self._mTotalChars <= 0 or self._mFreqChars <= MINIMUM_DATA_THRESHOLD:
return SURE_NO
if self._mTotalChars != self._mFreqChars:

View File

@@ -18,9 +18,17 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
import sys
if sys.version_info < (3, 0):
base_str = (str, unicode)
else:
base_str = (bytes, str)
def wrap_ord(a):
if isinstance(a, str):
if sys.version_info < (3, 0) and isinstance(a, base_str):
return ord(a)
elif isinstance(a, int):
else:
return a

View File

@@ -0,0 +1,44 @@
######################## BEGIN LICENSE BLOCK ########################
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Netscape Communications Corporation.
# Portions created by the Initial Developer are Copyright (C) 1998
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Mark Pilgrim - port to Python
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
from .mbcharsetprober import MultiByteCharSetProber
from .codingstatemachine import CodingStateMachine
from .chardistribution import EUCKRDistributionAnalysis
from .mbcssm import CP949SMModel
class CP949Prober(MultiByteCharSetProber):
def __init__(self):
MultiByteCharSetProber.__init__(self)
self._mCodingSM = CodingStateMachine(CP949SMModel)
# NOTE: CP949 is a superset of EUC-KR, so the distribution should be
# not different.
self._mDistributionAnalyzer = EUCKRDistributionAnalysis()
self.reset()
def get_charset_name(self):
return "CP949"

View File

@@ -25,8 +25,6 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
from . import constants
# KOI8-R language model
# Character Mapping Table:
KOI8R_CharToOrderMap = (

View File

@@ -25,8 +25,6 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
from . import constants
# 255: Control characters that usually does not exist in any text
# 254: Carriage/Return
# 253: symbol (punctuation) that does not belong to word

View File

@@ -27,8 +27,6 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
from . import constants
# 255: Control characters that usually does not exist in any text
# 254: Carriage/Return
# 253: symbol (punctuation) that does not belong to word

View File

@@ -25,8 +25,6 @@
# 02110-1301 USA
######################### END LICENSE BLOCK #########################
from . import constants
# 255: Control characters that usually does not exist in any text
# 254: Carriage/Return
# 253: symbol (punctuation) that does not belong to word

View File

@@ -33,6 +33,7 @@ from .sjisprober import SJISProber
from .eucjpprober import EUCJPProber
from .gb2312prober import GB2312Prober
from .euckrprober import EUCKRProber
from .cp949prober import CP949Prober
from .big5prober import Big5Prober
from .euctwprober import EUCTWProber
@@ -46,6 +47,7 @@ class MBCSGroupProber(CharSetGroupProber):
EUCJPProber(),
GB2312Prober(),
EUCKRProber(),
CP949Prober(),
Big5Prober(),
EUCTWProber()
]

View File

@@ -78,6 +78,46 @@ Big5SMModel = {'classTable': BIG5_cls,
'charLenTable': Big5CharLenTable,
'name': 'Big5'}
# CP949
CP949_cls = (
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f
1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f
1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f
4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f
1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f
5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f
0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f
6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f
6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af
7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf
7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef
2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff
)
CP949_st = (
#cls= 0 1 2 3 4 5 6 7 8 9 # previous state =
eError,eStart, 3,eError,eStart,eStart, 4, 5,eError, 6, # eStart
eError,eError,eError,eError,eError,eError,eError,eError,eError,eError, # eError
eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe, # eItsMe
eError,eError,eStart,eStart,eError,eError,eError,eStart,eStart,eStart, # 3
eError,eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 4
eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 5
eError,eStart,eStart,eStart,eStart,eError,eError,eStart,eStart,eStart, # 6
)
CP949CharLenTable = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2)
CP949SMModel = {'classTable': CP949_cls,
'classFactor': 10,
'stateTable': CP949_st,
'charLenTable': CP949CharLenTable,
'name': 'CP949'}
# EUC-JP
EUCJP_cls = (

View File

@@ -28,6 +28,7 @@
from . import constants
import sys
import codecs
from .latin1prober import Latin1Prober # windows-1252
from .mbcsgroupprober import MBCSGroupProber # multi-byte character sets
from .sbcsgroupprober import SBCSGroupProber # single-byte character sets
@@ -70,31 +71,31 @@ class UniversalDetector:
if not self._mGotData:
# If the data starts with BOM, we know it is UTF
if aBuf[:3] == '\xEF\xBB\xBF':
if aBuf[:3] == codecs.BOM:
# EF BB BF UTF-8 with BOM
self.result = {'encoding': "UTF-8", 'confidence': 1.0}
elif aBuf[:4] == '\xFF\xFE\x00\x00':
elif aBuf[:4] == codecs.BOM_UTF32_LE:
# FF FE 00 00 UTF-32, little-endian BOM
self.result = {'encoding': "UTF-32LE", 'confidence': 1.0}
elif aBuf[:4] == '\x00\x00\xFE\xFF':
elif aBuf[:4] == codecs.BOM_UTF32_BE:
# 00 00 FE FF UTF-32, big-endian BOM
self.result = {'encoding': "UTF-32BE", 'confidence': 1.0}
elif aBuf[:4] == '\xFE\xFF\x00\x00':
elif aBuf[:4] == b'\xFE\xFF\x00\x00':
# FE FF 00 00 UCS-4, unusual octet order BOM (3412)
self.result = {
'encoding': "X-ISO-10646-UCS-4-3412",
'confidence': 1.0
}
elif aBuf[:4] == '\x00\x00\xFF\xFE':
elif aBuf[:4] == b'\x00\x00\xFF\xFE':
# 00 00 FF FE UCS-4, unusual octet order BOM (2143)
self.result = {
'encoding': "X-ISO-10646-UCS-4-2143",
'confidence': 1.0
}
elif aBuf[:2] == '\xFF\xFE':
elif aBuf[:2] == codecs.BOM_LE:
# FF FE UTF-16, little endian BOM
self.result = {'encoding': "UTF-16LE", 'confidence': 1.0}
elif aBuf[:2] == '\xFE\xFF':
elif aBuf[:2] == codecs.BOM_BE:
# FE FF UTF-16, big endian BOM
self.result = {'encoding': "UTF-16BE", 'confidence': 1.0}

View File

@@ -1,5 +1,5 @@
# urllib3/__init__.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

View File

@@ -1,5 +1,5 @@
# urllib3/_collections.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

View File

@@ -1,5 +1,5 @@
# urllib3/connectionpool.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -9,6 +9,7 @@ import socket
import errno
from socket import error as SocketError, timeout as SocketTimeout
from .util import resolve_cert_reqs, resolve_ssl_version, assert_fingerprint
try: # Python 3
from http.client import HTTPConnection, HTTPException
@@ -80,33 +81,38 @@ class VerifiedHTTPSConnection(HTTPSConnection):
ssl_version = None
def set_cert(self, key_file=None, cert_file=None,
cert_reqs='CERT_NONE', ca_certs=None):
ssl_req_scheme = {
'CERT_NONE': ssl.CERT_NONE,
'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
'CERT_REQUIRED': ssl.CERT_REQUIRED
}
cert_reqs=None, ca_certs=None,
assert_hostname=None, assert_fingerprint=None):
self.key_file = key_file
self.cert_file = cert_file
self.cert_reqs = ssl_req_scheme.get(cert_reqs) or ssl.CERT_NONE
self.cert_reqs = cert_reqs
self.ca_certs = ca_certs
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint
def connect(self):
# Add certificate verification
sock = socket.create_connection((self.host, self.port), self.timeout)
resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
resolved_ssl_version = resolve_ssl_version(self.ssl_version)
# Wrap socket using verification with the root certs in
# trusted_root_certs
self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
cert_reqs=self.cert_reqs,
cert_reqs=resolved_cert_reqs,
ca_certs=self.ca_certs,
server_hostname=self.host,
ssl_version=self.ssl_version)
if self.ca_certs:
match_hostname(self.sock.getpeercert(), self.host)
ssl_version=resolved_ssl_version)
if resolved_cert_reqs != ssl.CERT_NONE:
if self.assert_fingerprint:
assert_fingerprint(self.sock.getpeercert(binary_form=True),
self.assert_fingerprint)
else:
match_hostname(self.sock.getpeercert(),
self.assert_hostname or self.host)
## Pool objects
@@ -440,12 +446,14 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
except Empty as e:
# Timed out by queue
raise TimeoutError(self, "Request timed out. (pool_timeout=%s)" %
raise TimeoutError(self, url,
"Request timed out. (pool_timeout=%s)" %
pool_timeout)
except SocketTimeout as e:
# Timed out by socket
raise TimeoutError(self, "Request timed out. (timeout=%s)" %
raise TimeoutError(self, url,
"Request timed out. (timeout=%s)" %
timeout)
except BaseSSLError as e:
@@ -503,9 +511,13 @@ class HTTPSConnectionPool(HTTPConnectionPool):
:class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates,
instead of :class:`httplib.HTTPSConnection`.
The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, and ``ssl_version``
are only used if :mod:`ssl` is available and are fed into
:meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket into an SSL socket.
:class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``,
``assert_hostname`` and ``host`` in this order to verify connections.
The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and
``ssl_version`` are only used if :mod:`ssl` is available and are fed into
:meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket
into an SSL socket.
"""
scheme = 'https'
@@ -513,8 +525,9 @@ class HTTPSConnectionPool(HTTPConnectionPool):
def __init__(self, host, port=None,
strict=False, timeout=None, maxsize=1,
block=False, headers=None,
key_file=None, cert_file=None,
cert_reqs='CERT_NONE', ca_certs=None, ssl_version=None):
key_file=None, cert_file=None, cert_reqs=None,
ca_certs=None, ssl_version=None,
assert_hostname=None, assert_fingerprint=None):
HTTPConnectionPool.__init__(self, host, port,
strict, timeout, maxsize,
@@ -524,6 +537,8 @@ class HTTPSConnectionPool(HTTPConnectionPool):
self.cert_reqs = cert_reqs
self.ca_certs = ca_certs
self.ssl_version = ssl_version
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint
def _new_conn(self):
"""
@@ -533,7 +548,7 @@ class HTTPSConnectionPool(HTTPConnectionPool):
log.info("Starting new HTTPS connection (%d): %s"
% (self.num_connections, self.host))
if not ssl: # Platform-specific: Python compiled without +ssl
if not ssl: # Platform-specific: Python compiled without +ssl
if not HTTPSConnection or HTTPSConnection is object:
raise SSLError("Can't connect to HTTPS URL because the SSL "
"module is not available.")
@@ -546,12 +561,11 @@ class HTTPSConnectionPool(HTTPConnectionPool):
port=self.port,
strict=self.strict)
connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)
cert_reqs=self.cert_reqs, ca_certs=self.ca_certs,
assert_hostname=self.assert_hostname,
assert_fingerprint=self.assert_fingerprint)
if self.ssl_version is None:
connection.ssl_version = ssl.PROTOCOL_SSLv23
else:
connection.ssl_version = self.ssl_version
connection.ssl_version = self.ssl_version
return connection

View File

@@ -1,5 +1,5 @@
# urllib3/contrib/ntlmpool.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

View File

@@ -0,0 +1,167 @@
'''SSL with SNI-support for Python 2.
This needs the following packages installed:
* pyOpenSSL (tested with 0.13)
* ndg-httpsclient (tested with 0.3.2)
* pyasn1 (tested with 0.1.6)
To activate it call :func:`~urllib3.contrib.pyopenssl.inject_into_urllib3`.
This can be done in a ``sitecustomize`` module, or at any other time before
your application begins using ``urllib3``, like this::
try:
import urllib3.contrib.pyopenssl
urllib3.contrib.pyopenssl.inject_into_urllib3()
except ImportError:
pass
Now you can use :mod:`urllib3` as you normally would, and it will support SNI
when the required modules are installed.
'''
from ndg.httpsclient.ssl_peer_verification import (ServerSSLCertVerification,
SUBJ_ALT_NAME_SUPPORT)
from ndg.httpsclient.subj_alt_name import SubjectAltName
import OpenSSL.SSL
from pyasn1.codec.der import decoder as der_decoder
from socket import _fileobject
import ssl
from .. import connectionpool
from .. import util
__all__ = ['inject_into_urllib3', 'extract_from_urllib3']
# SNI only *really* works if we can read the subjectAltName of certificates.
HAS_SNI = SUBJ_ALT_NAME_SUPPORT
# Map from urllib3 to PyOpenSSL compatible parameter-values.
_openssl_versions = {
ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD,
ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD,
ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
}
_openssl_verify = {
ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,
ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,
ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER
+ OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
}
orig_util_HAS_SNI = util.HAS_SNI
orig_connectionpool_ssl_wrap_socket = connectionpool.ssl_wrap_socket
def inject_into_urllib3():
'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.'
connectionpool.ssl_wrap_socket = ssl_wrap_socket
util.HAS_SNI = HAS_SNI
def extract_from_urllib3():
'Undo monkey-patching by :func:`inject_into_urllib3`.'
connectionpool.ssl_wrap_socket = orig_connectionpool_ssl_wrap_socket
util.HAS_SNI = orig_util_HAS_SNI
### Note: This is a slightly bug-fixed version of same from ndg-httpsclient.
def get_subj_alt_name(peer_cert):
# Search through extensions
dns_name = []
if not SUBJ_ALT_NAME_SUPPORT:
return dns_name
general_names = SubjectAltName()
for i in range(peer_cert.get_extension_count()):
ext = peer_cert.get_extension(i)
ext_name = ext.get_short_name()
if ext_name != 'subjectAltName':
continue
# PyOpenSSL returns extension data in ASN.1 encoded form
ext_dat = ext.get_data()
decoded_dat = der_decoder.decode(ext_dat,
asn1Spec=general_names)
for name in decoded_dat:
if not isinstance(name, SubjectAltName):
continue
for entry in range(len(name)):
component = name.getComponentByPosition(entry)
if component.getName() != 'dNSName':
continue
dns_name.append(str(component.getComponent()))
return dns_name
class WrappedSocket(object):
'''API-compatibility wrapper for Python OpenSSL's Connection-class.'''
def __init__(self, connection, socket):
self.connection = connection
self.socket = socket
def makefile(self, mode, bufsize=-1):
return _fileobject(self.connection, mode, bufsize)
def settimeout(self, timeout):
return self.socket.settimeout(timeout)
def sendall(self, data):
return self.connection.sendall(data)
def getpeercert(self, binary_form=False):
x509 = self.connection.get_peer_certificate()
if not x509:
raise ssl.SSLError('')
if binary_form:
return OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_ASN1,
x509)
return {
'subject': (
(('commonName', x509.get_subject().CN),),
),
'subjectAltName': [
('DNS', value)
for value in get_subj_alt_name(x509)
]
}
def _verify_callback(cnx, x509, err_no, err_depth, return_code):
return err_no == 0
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
ca_certs=None, server_hostname=None,
ssl_version=None):
ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version])
if certfile:
ctx.use_certificate_file(certfile)
if keyfile:
ctx.use_privatekey_file(keyfile)
if cert_reqs != ssl.CERT_NONE:
ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback)
if ca_certs:
try:
ctx.load_verify_locations(ca_certs, None)
except OpenSSL.SSL.Error as e:
raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e)
cnx = OpenSSL.SSL.Connection(ctx, sock)
cnx.set_tlsext_host_name(server_hostname)
cnx.set_connect_state()
try:
cnx.do_handshake()
except OpenSSL.SSL.Error as e:
raise ssl.SSLError('bad handshake', e)
return WrappedSocket(cnx, sock)

View File

@@ -1,5 +1,5 @@
# urllib3/exceptions.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -20,7 +20,18 @@ class PoolError(HTTPError):
def __reduce__(self):
# For pickling purposes.
return self.__class__, (None, self.url)
return self.__class__, (None, None)
class RequestError(PoolError):
"Base exception for PoolErrors that have associated URLs."
def __init__(self, pool, url, message):
self.url = url
PoolError.__init__(self, pool, message)
def __reduce__(self):
# For pickling purposes.
return self.__class__, (None, self.url, None)
class SSLError(HTTPError):
@@ -35,7 +46,7 @@ class DecodeError(HTTPError):
## Leaf Exceptions
class MaxRetryError(PoolError):
class MaxRetryError(RequestError):
"Raised when the maximum number of retries is exceeded."
def __init__(self, pool, url, reason=None):
@@ -47,22 +58,19 @@ class MaxRetryError(PoolError):
else:
message += " (Caused by redirect)"
PoolError.__init__(self, pool, message)
self.url = url
RequestError.__init__(self, pool, url, message)
class HostChangedError(PoolError):
class HostChangedError(RequestError):
"Raised when an existing pool gets a request for a foreign host."
def __init__(self, pool, url, retries=3):
message = "Tried to open a foreign host with url: %s" % url
PoolError.__init__(self, pool, message)
self.url = url
RequestError.__init__(self, pool, url, message)
self.retries = retries
class TimeoutError(PoolError):
class TimeoutError(RequestError):
"Raised when a socket timeout occurs."
pass

View File

@@ -93,6 +93,6 @@ def encode_multipart_formdata(fields, boundary=None):
body.write(b('--%s--\r\n' % (boundary)))
content_type = b('multipart/form-data; boundary=%s' % boundary)
content_type = str('multipart/form-data; boundary=%s' % boundary)
return body.getvalue(), content_type

View File

@@ -1,5 +1,5 @@
# urllib3/poolmanager.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -23,6 +23,9 @@ pool_classes_by_scheme = {
log = logging.getLogger(__name__)
SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs',
'ssl_version')
class PoolManager(RequestMethods):
"""
@@ -58,6 +61,23 @@ class PoolManager(RequestMethods):
self.pools = RecentlyUsedContainer(num_pools,
dispose_func=lambda p: p.close())
def _new_pool(self, scheme, host, port):
"""
Create a new :class:`ConnectionPool` based on host, port and scheme.
This method is used to actually create the connection pools handed out
by :meth:`connection_from_url` and companion methods. It is intended
to be overridden for customization.
"""
pool_cls = pool_classes_by_scheme[scheme]
kwargs = self.connection_pool_kw
if scheme == 'http':
kwargs = self.connection_pool_kw.copy()
for kw in SSL_KEYWORDS:
kwargs.pop(kw, None)
return pool_cls(host, port, **kwargs)
def clear(self):
"""
Empty our store of pools and direct them all to close.
@@ -74,6 +94,7 @@ class PoolManager(RequestMethods):
If ``port`` isn't given, it will be derived from the ``scheme`` using
``urllib3.connectionpool.port_by_scheme``.
"""
scheme = scheme or 'http'
port = port or port_by_scheme.get(scheme, 80)
pool_key = (scheme, host, port)
@@ -85,11 +106,8 @@ class PoolManager(RequestMethods):
return pool
# Make a fresh ConnectionPool of the desired type
pool_cls = pool_classes_by_scheme[scheme]
pool = pool_cls(host, port, **self.connection_pool_kw)
pool = self._new_pool(scheme, host, port)
self.pools[pool_key] = pool
return pool
def connection_from_url(self, url):
@@ -132,20 +150,31 @@ class PoolManager(RequestMethods):
log.info("Redirecting %s -> %s" % (url, redirect_location))
kw['retries'] = kw.get('retries', 3) - 1 # Persist retries countdown
kw['redirect'] = redirect
return self.urlopen(method, redirect_location, **kw)
class ProxyManager(RequestMethods):
"""
Given a ConnectionPool to a proxy, the ProxyManager's ``urlopen`` method
will make requests to any url through the defined proxy.
will make requests to any url through the defined proxy. The ProxyManager
class will automatically set the 'Host' header if it is not provided.
"""
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
def _set_proxy_headers(self, headers=None):
def _set_proxy_headers(self, url, headers=None):
"""
Sets headers needed by proxies: specifically, the Accept and Host
headers. Only sets headers not provided by the user.
"""
headers_ = {'Accept': '*/*'}
host = parse_url(url).host
if host:
headers_['Host'] = host
if headers:
headers_.update(headers)
@@ -154,7 +183,7 @@ class ProxyManager(RequestMethods):
def urlopen(self, method, url, **kw):
"Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute."
kw['assert_same_host'] = False
kw['headers'] = self._set_proxy_headers(kw.get('headers'))
kw['headers'] = self._set_proxy_headers(url, headers=kw.get('headers'))
return self.proxy_pool.urlopen(method, url, **kw)

View File

@@ -1,5 +1,5 @@
# urllib3/request.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

View File

@@ -4,29 +4,48 @@
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
import gzip
import logging
import zlib
from io import BytesIO
from .exceptions import DecodeError
from .packages.six import string_types as basestring
from .packages.six import string_types as basestring, binary_type
log = logging.getLogger(__name__)
def decode_gzip(data):
gzipper = gzip.GzipFile(fileobj=BytesIO(data))
return gzipper.read()
class DeflateDecoder(object):
def __init__(self):
self._first_try = True
self._data = binary_type()
self._obj = zlib.decompressobj()
def __getattr__(self, name):
return getattr(self._obj, name)
def decompress(self, data):
if not self._first_try:
return self._obj.decompress(data)
self._data += data
try:
return self._obj.decompress(data)
except zlib.error:
self._first_try = False
self._obj = zlib.decompressobj(-zlib.MAX_WBITS)
try:
return self.decompress(self._data)
finally:
self._data = None
def decode_deflate(data):
try:
return zlib.decompress(data)
except zlib.error:
return zlib.decompress(data, -zlib.MAX_WBITS)
def _get_decoder(mode):
if mode == 'gzip':
return zlib.decompressobj(16 + zlib.MAX_WBITS)
return DeflateDecoder()
class HTTPResponse(object):
@@ -52,10 +71,7 @@ class HTTPResponse(object):
otherwise unused.
"""
CONTENT_DECODERS = {
'gzip': decode_gzip,
'deflate': decode_deflate,
}
CONTENT_DECODERS = ['gzip', 'deflate']
def __init__(self, body='', headers=None, status=0, version=0, reason=None,
strict=0, preload_content=True, decode_content=True,
@@ -65,8 +81,9 @@ class HTTPResponse(object):
self.version = version
self.reason = reason
self.strict = strict
self.decode_content = decode_content
self._decode_content = decode_content
self._decoder = None
self._body = body if body and isinstance(body, basestring) else None
self._fp = None
self._original_response = original_response
@@ -115,13 +132,13 @@ class HTTPResponse(object):
parameters: ``decode_content`` and ``cache_content``.
:param amt:
How much of the content to read. If specified, decoding and caching
is skipped because we can't decode partial content nor does it make
sense to cache partial content as the full response.
How much of the content to read. If specified, caching is skipped
because it doesn't make sense to cache partial content as the full
response.
:param decode_content:
If True, will attempt to decode the body based on the
'content-encoding' header. (Overridden if ``amt`` is set.)
'content-encoding' header.
:param cache_content:
If True, will save the returned data such that the same result is
@@ -133,27 +150,47 @@ class HTTPResponse(object):
# Note: content-encoding value should be case-insensitive, per RFC 2616
# Section 3.5
content_encoding = self.headers.get('content-encoding', '').lower()
decoder = self.CONTENT_DECODERS.get(content_encoding)
if self._decoder is None:
if content_encoding in self.CONTENT_DECODERS:
self._decoder = _get_decoder(content_encoding)
if decode_content is None:
decode_content = self._decode_content
decode_content = self.decode_content
if self._fp is None:
return
flush_decoder = False
try:
if amt is None:
# cStringIO doesn't like amt=None
data = self._fp.read()
flush_decoder = True
else:
return self._fp.read(amt)
cache_content = False
data = self._fp.read(amt)
if amt != 0 and not data: # Platform-specific: Buggy versions of Python.
# Close the connection when no data is returned
#
# This is redundant to what httplib/http.client _should_
# already do. However, versions of python released before
# December 15, 2012 (http://bugs.python.org/issue16298) do not
# properly close the connection in all cases. There is no harm
# in redundantly calling close.
self._fp.close()
flush_decoder = True
try:
if decode_content and decoder:
data = decoder(data)
if decode_content and self._decoder:
data = self._decoder.decompress(data)
except (IOError, zlib.error):
raise DecodeError("Received response with content-encoding: %s, but "
"failed to decode it." % content_encoding)
if flush_decoder and self._decoder:
buf = self._decoder.decompress(binary_type())
data += buf + self._decoder.flush()
if cache_content:
self._body = data

View File

@@ -1,5 +1,5 @@
# urllib3/util.py
# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
#
# This module is part of urllib3 and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -8,6 +8,8 @@
from base64 import b64encode
from collections import namedtuple
from socket import error as SocketError
from hashlib import md5, sha1
from binascii import hexlify, unhexlify
try:
from select import poll, POLLIN
@@ -22,7 +24,8 @@ try: # Test for SSL features
SSLContext = None
HAS_SNI = False
from ssl import wrap_socket, CERT_NONE, SSLError, PROTOCOL_SSLv23
import ssl
from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
from ssl import SSLContext # Modern SSL?
from ssl import HAS_SNI # Has SNI?
except ImportError:
@@ -30,7 +33,7 @@ except ImportError:
from .packages import six
from .exceptions import LocationParseError
from .exceptions import LocationParseError, SSLError
class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])):
@@ -231,7 +234,7 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
return headers
def is_connection_dropped(conn):
def is_connection_dropped(conn): # Platform-specific
"""
Returns True if the connection is dropped and should be closed.
@@ -245,7 +248,7 @@ def is_connection_dropped(conn):
if not sock: # Platform-specific: AppEngine
return False
if not poll: # Platform-specific
if not poll:
if not select: # Platform-specific: AppEngine
return False
@@ -263,10 +266,86 @@ def is_connection_dropped(conn):
return True
def resolve_cert_reqs(candidate):
"""
Resolves the argument to a numeric constant, which can be passed to
the wrap_socket function/method from the ssl module.
Defaults to :data:`ssl.CERT_NONE`.
If given a string it is assumed to be the name of the constant in the
:mod:`ssl` module or its abbrevation.
(So you can specify `REQUIRED` instead of `CERT_REQUIRED`.
If it's neither `None` nor a string we assume it is already the numeric
constant which can directly be passed to wrap_socket.
"""
if candidate is None:
return CERT_NONE
if isinstance(candidate, str):
res = getattr(ssl, candidate, None)
if res is None:
res = getattr(ssl, 'CERT_' + candidate)
return res
return candidate
def resolve_ssl_version(candidate):
"""
like resolve_cert_reqs
"""
if candidate is None:
return PROTOCOL_SSLv23
if isinstance(candidate, str):
res = getattr(ssl, candidate, None)
if res is None:
res = getattr(ssl, 'PROTOCOL_' + candidate)
return res
return candidate
def assert_fingerprint(cert, fingerprint):
"""
Checks if given fingerprint matches the supplied certificate.
:param cert:
Certificate as bytes object.
:param fingerprint:
Fingerprint as string of hexdigits, can be interspersed by colons.
"""
# Maps the length of a digest to a possible hash function producing
# this digest.
hashfunc_map = {
16: md5,
20: sha1
}
fingerprint = fingerprint.replace(':', '').lower()
digest_length, rest = divmod(len(fingerprint), 2)
if rest or digest_length not in hashfunc_map:
raise SSLError('Fingerprint is of invalid length.')
# We need encode() here for py32; works on py2 and p33.
fingerprint_bytes = unhexlify(fingerprint.encode())
hashfunc = hashfunc_map[digest_length]
cert_digest = hashfunc(cert).digest()
if not cert_digest == fingerprint_bytes:
raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".'
.format(hexlify(fingerprint_bytes),
hexlify(cert_digest)))
if SSLContext is not None: # Python 3.2+
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=CERT_NONE,
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
ca_certs=None, server_hostname=None,
ssl_version=PROTOCOL_SSLv23):
ssl_version=None):
"""
All arguments except `server_hostname` have the same meaning as for
:func:`ssl.wrap_socket`
@@ -279,8 +358,9 @@ if SSLContext is not None: # Python 3.2+
if ca_certs:
try:
context.load_verify_locations(ca_certs)
except TypeError as e: # Reraise as SSLError
# FIXME: This block needs a test.
# Py32 raises IOError
# Py33 raises FileNotFoundError
except Exception as e: # Reraise as SSLError
raise SSLError(e)
if certfile:
# FIXME: This block needs a test.
@@ -290,9 +370,9 @@ if SSLContext is not None: # Python 3.2+
return context.wrap_socket(sock)
else: # Python 3.1 and earlier
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=CERT_NONE,
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
ca_certs=None, server_hostname=None,
ssl_version=PROTOCOL_SSLv23):
ssl_version=None):
return wrap_socket(sock, keyfile=keyfile, certfile=certfile,
ca_certs=ca_certs, cert_reqs=cert_reqs,
ssl_version=ssl_version)

View File

@@ -9,21 +9,26 @@ requests (cookies, auth, proxies).
"""
import os
from datetime import datetime
from .compat import cookielib
from .cookies import cookiejar_from_dict
from .models import Request
from .hooks import dispatch_hook, default_hooks
from .compat import cookielib, OrderedDict, urljoin, urlparse
from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar
from .models import Request, PreparedRequest
from .hooks import default_hooks, dispatch_hook
from .utils import from_key_val_list, default_headers
from .exceptions import TooManyRedirects, InvalidSchema
from .compat import urlparse, urljoin
from .adapters import HTTPAdapter
from .utils import requote_uri, get_environ_proxies, get_netrc_auth
from .status_codes import codes
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
REDIRECT_STATI = (
codes.moved, # 301
codes.found, # 302
codes.other, # 303
codes.temporary_moved, # 307
)
DEFAULT_REDIRECT_LIMIT = 30
@@ -49,9 +54,20 @@ def merge_kwargs(local_kwarg, default_kwarg):
default_kwarg = from_key_val_list(default_kwarg)
local_kwarg = from_key_val_list(local_kwarg)
# Update new values.
# Update new values in a case-insensitive way
def get_original_key(original_keys, new_key):
"""
Finds the key from original_keys that case-insensitive matches new_key.
"""
for original_key in original_keys:
if key.lower() == original_key.lower():
return original_key
return new_key
kwargs = default_kwarg.copy()
kwargs.update(local_kwarg)
original_keys = kwargs.keys()
for key, value in local_kwarg.items():
kwargs[get_original_key(original_keys, key)] = value
# Remove keys that are set to None.
for (k, v) in local_kwarg.items():
@@ -62,11 +78,17 @@ def merge_kwargs(local_kwarg, default_kwarg):
class SessionRedirectMixin(object):
def resolve_redirects(self, resp, req, stream=False, timeout=None, verify=True, cert=None, proxies=None):
def resolve_redirects(self, resp, req, stream=False, timeout=None,
verify=True, cert=None, proxies=None):
"""Receives a Response. Returns a generator of Responses."""
i = 0
prepared_request = PreparedRequest()
prepared_request.body = req.body
prepared_request.headers = req.headers.copy()
prepared_request.hooks = req.hooks
prepared_request.method = req.method
prepared_request.url = req.url
# ((resp.status_code is codes.see_other))
while (('location' in resp.headers and resp.status_code in REDIRECT_STATI)):
@@ -80,7 +102,7 @@ class SessionRedirectMixin(object):
resp.close()
url = resp.headers['location']
method = req.method
method = prepared_request.method
# Handle redirection without scheme (see: RFC 1808 Section 4)
if url.startswith('//'):
@@ -93,35 +115,46 @@ class SessionRedirectMixin(object):
# Compliant with RFC3986, we percent encode the url.
url = urljoin(resp.url, requote_uri(url))
prepared_request.url = url
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
if resp.status_code is codes.see_other and req.method != 'HEAD':
if (resp.status_code == codes.see_other and
prepared_request.method != 'HEAD'):
method = 'GET'
# Do what the browsers do, despite standards...
if resp.status_code in (codes.moved, codes.found) and req.method == 'POST':
if (resp.status_code in (codes.moved, codes.found) and
prepared_request.method not in ('GET', 'HEAD')):
method = 'GET'
# Remove the cookie headers that were sent.
headers = req.headers
prepared_request.method = method
# https://github.com/kennethreitz/requests/issues/1084
if resp.status_code not in (codes.temporary, codes.resume):
if 'Content-Length' in prepared_request.headers:
del prepared_request.headers['Content-Length']
prepared_request.body = None
headers = prepared_request.headers
try:
del headers['Cookie']
except KeyError:
pass
resp = self.request(
url=url,
method=method,
headers=headers,
params=req.params,
auth=req.auth,
cookies=req.cookies,
allow_redirects=False,
stream=stream,
timeout=timeout,
verify=verify,
cert=cert,
proxies=proxies
)
prepared_request.prepare_cookies(self.cookies)
resp = self.send(
prepared_request,
stream=stream,
timeout=timeout,
verify=verify,
cert=cert,
proxies=proxies,
allow_redirects=False,
)
extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
i += 1
yield resp
@@ -140,6 +173,11 @@ class Session(SessionRedirectMixin):
200
"""
__attrs__ = [
'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks',
'params', 'verify', 'cert', 'prefetch', 'adapters', 'stream',
'trust_env', 'max_redirects']
def __init__(self):
#: A case-insensitive dictionary of headers to be sent on each
@@ -173,7 +211,8 @@ class Session(SessionRedirectMixin):
#: SSL certificate default.
self.cert = None
#: Maximum number of redirects to follow.
#: Maximum number of redirects allowed. If the request exceeds this
#: limit, a :class:`TooManyRedirects` exception is raised.
self.max_redirects = DEFAULT_REDIRECT_LIMIT
#: Should we trust the environment?
@@ -183,9 +222,9 @@ class Session(SessionRedirectMixin):
self.cookies = cookiejar_from_dict({})
# Default connection adapters.
self.adapters = {}
self.mount('http://', HTTPAdapter())
self.adapters = OrderedDict()
self.mount('https://', HTTPAdapter())
self.mount('http://', HTTPAdapter())
def __enter__(self):
return self
@@ -207,6 +246,35 @@ class Session(SessionRedirectMixin):
stream=None,
verify=None,
cert=None):
"""Constructs a :class:`Request <Request>`, prepares it and sends it.
Returns :class:`Response <Response>` object.
:param method: method for the new :class:`Request` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query
string for the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the
:class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the
:class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the
:class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects
for multipart encoding upload.
:param auth: (optional) Auth tuple or callable to enable
Basic/Digest/Custom HTTP Auth.
:param timeout: (optional) Float describing the timeout of the
request.
:param allow_redirects: (optional) Boolean. Set to True by default.
:param proxies: (optional) Dictionary mapping protocol to the URL of
the proxy.
:param stream: (optional) whether to immediately download the response
content. Defaults to ``False``.
:param verify: (optional) if ``True``, the SSL cert will be verified.
A CA_BUNDLE path can also be provided.
:param cert: (optional) if String, path to ssl client cert file (.pem).
If Tuple, ('cert', 'key') pair.
"""
cookies = cookies or {}
proxies = proxies or {}
@@ -215,9 +283,11 @@ class Session(SessionRedirectMixin):
if not isinstance(cookies, cookielib.CookieJar):
cookies = cookiejar_from_dict(cookies)
# Bubble down session cookies.
for cookie in self.cookies:
cookies.set_cookie(cookie)
# Merge with session cookies
merged_cookies = RequestsCookieJar()
merged_cookies.update(self.cookies)
merged_cookies.update(cookies)
cookies = merged_cookies
# Gather clues from the surrounding environment.
if self.trust_env:
@@ -238,7 +308,6 @@ class Session(SessionRedirectMixin):
if not verify and verify is not False:
verify = os.environ.get('CURL_CA_BUNDLE')
# Merge all the kwargs.
params = merge_kwargs(params, self.params)
headers = merge_kwargs(headers, self.headers)
@@ -249,7 +318,6 @@ class Session(SessionRedirectMixin):
verify = merge_kwargs(verify, self.verify)
cert = merge_kwargs(cert, self.cert)
# Create the Request.
req = Request()
req.method = method.upper()
@@ -266,26 +334,15 @@ class Session(SessionRedirectMixin):
prep = req.prepare()
# Send the request.
resp = self.send(prep, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
# Persist cookies.
for cookie in resp.cookies:
self.cookies.set_cookie(cookie)
# Redirect resolving generator.
gen = self.resolve_redirects(resp, req, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
# Resolve redirects if allowed.
history = [r for r in gen] if allow_redirects else []
# Shuffle things around if there's history.
if history:
history.insert(0, resp)
resp = history.pop()
resp.history = tuple(history)
# Response manipulation hook.
self.response = dispatch_hook('response', hooks, resp)
send_kwargs = {
'stream': stream,
'timeout': timeout,
'verify': verify,
'cert': cert,
'proxies': proxies,
'allow_redirects': allow_redirects,
}
resp = self.send(prep, **send_kwargs)
return resp
@@ -360,8 +417,60 @@ class Session(SessionRedirectMixin):
def send(self, request, **kwargs):
"""Send a given PreparedRequest."""
# Set defaults that the hooks can utilize to ensure they always have
# the correct parameters to reproduce the previous request.
kwargs.setdefault('stream', self.stream)
kwargs.setdefault('verify', self.verify)
kwargs.setdefault('cert', self.cert)
kwargs.setdefault('proxies', self.proxies)
# It's possible that users might accidentally send a Request object.
# Guard against that specific failure case.
if getattr(request, 'prepare', None):
raise ValueError('You can only send PreparedRequests.')
# Set up variables needed for resolve_redirects and dispatching of
# hooks
allow_redirects = kwargs.pop('allow_redirects', True)
stream = kwargs.get('stream')
timeout = kwargs.get('timeout')
verify = kwargs.get('verify')
cert = kwargs.get('cert')
proxies = kwargs.get('proxies')
hooks = request.hooks
# Get the appropriate adapter to use
adapter = self.get_adapter(url=request.url)
# Start time (approximately) of the request
start = datetime.utcnow()
# Send the request
r = adapter.send(request, **kwargs)
# Total elapsed time of the request (approximately)
r.elapsed = datetime.utcnow() - start
# Response manipulation hooks
r = dispatch_hook('response', hooks, r, **kwargs)
# Persist cookies
extract_cookies_to_jar(self.cookies, request, r.raw)
# Redirect resolving generator.
gen = self.resolve_redirects(r, request, stream=stream,
timeout=timeout, verify=verify, cert=cert,
proxies=proxies)
# Resolve redirects if allowed.
history = [resp for resp in gen] if allow_redirects else []
# Shuffle things around if there's history.
if history:
# Insert the first (original) request at the start
history.insert(0, r)
# Get the last request made
r = history.pop()
r.history = tuple(history)
return r
def get_adapter(self, url):
@@ -380,8 +489,13 @@ class Session(SessionRedirectMixin):
v.close()
def mount(self, prefix, adapter):
"""Registers a connection adapter to a prefix."""
"""Registers a connection adapter to a prefix.
Adapters are sorted in descending order by key length."""
self.adapters[prefix] = adapter
keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
for key in keys_to_move:
self.adapters[key] = self.adapters.pop(key)
def __getstate__(self):
return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)

View File

@@ -62,6 +62,7 @@ _codes = {
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),
# Server Error.

View File

@@ -8,44 +8,105 @@ Data structures that power Requests.
"""
import os
import collections
from itertools import islice
class CaseInsensitiveDict(dict):
"""Case-insensitive Dictionary
class IteratorProxy(object):
"""docstring for IteratorProxy"""
def __init__(self, i):
self.i = i
# self.i = chain.from_iterable(i)
def __iter__(self):
return self.i
def __len__(self):
if hasattr(self.i, '__len__'):
return len(self.i)
if hasattr(self.i, 'len'):
return self.i.len
if hasattr(self.i, 'fileno'):
return os.fstat(self.i.fileno()).st_size
def read(self, n):
return "".join(islice(self.i, None, n))
class CaseInsensitiveDict(collections.MutableMapping):
"""
A case-insensitive ``dict``-like object.
Implements all methods and operations of
``collections.MutableMapping`` as well as dict's ``copy``. Also
provides ``lower_items``.
All keys are expected to be strings. The structure remembers the
case of the last key to be set, and ``iter(instance)``,
``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
will contain case-sensitive keys. However, querying and contains
testing is case insensitive:
cid = CaseInsensitiveDict()
cid['Accept'] = 'application/json'
cid['aCCEPT'] == 'application/json' # True
list(cid) == ['Accept'] # True
For example, ``headers['content-encoding']`` will return the
value of a ``'Content-Encoding'`` response header."""
value of a ``'Content-Encoding'`` response header, regardless
of how the header name was originally stored.
@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 list(self.keys()))
return self._lower_keys
If the constructor, ``.update``, or equality comparison
operations are given keys that have equal ``.lower()``s, the
behavior is undefined.
def _clear_lower_keys(self):
if hasattr(self, '_lower_keys'):
self._lower_keys.clear()
"""
def __init__(self, data=None, **kwargs):
self._store = dict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self._clear_lower_keys()
def __delitem__(self, key):
dict.__delitem__(self, self.lower_keys.get(key.lower(), key))
self._lower_keys.clear()
def __contains__(self, key):
return key.lower() in self.lower_keys
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
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()])
return self._store[key.lower()][1]
def get(self, key, default=None):
if key in self:
return self[key]
def __delitem__(self, key):
del self._store[key.lower()]
def __iter__(self):
return (casedkey for casedkey, mappedvalue in self._store.values())
def __len__(self):
return len(self._store)
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
return (
(lowerkey, keyval[1])
for (lowerkey, keyval)
in self._store.items()
)
def __eq__(self, other):
if isinstance(other, collections.Mapping):
other = CaseInsensitiveDict(other)
else:
return default
return NotImplemented
# Compare insensitively
return dict(self.lower_items()) == dict(other.lower_items())
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, dict(self.items()))
class LookupDict(dict):

View File

@@ -19,49 +19,17 @@ import zlib
from netrc import netrc, NetrcParseError
from . import __version__
from . import certs
from .compat import parse_http_list as _parse_list_header
from .compat import quote, urlparse, bytes, str, OrderedDict
from .compat import quote, urlparse, bytes, str, OrderedDict, urlunparse
from .cookies import RequestsCookieJar, cookiejar_from_dict
from .structures import CaseInsensitiveDict
_hush_pyflakes = (RequestsCookieJar,)
CERTIFI_BUNDLE_PATH = None
try:
# see if requests's own CA certificate bundle is installed
from . import certs
path = certs.where()
if os.path.exists(path):
CERTIFI_BUNDLE_PATH = certs.where()
except ImportError:
pass
NETRC_FILES = ('.netrc', '_netrc')
# common paths for the OS's CA certificate bundle
POSSIBLE_CA_BUNDLE_PATHS = [
# Red Hat, CentOS, Fedora and friends (provided by the ca-certificates package):
'/etc/pki/tls/certs/ca-bundle.crt',
# Ubuntu, Debian, and friends (provided by the ca-certificates package):
'/etc/ssl/certs/ca-certificates.crt',
# FreeBSD (provided by the ca_root_nss package):
'/usr/local/share/certs/ca-root-nss.crt',
# openSUSE (provided by the ca-certificates package), the 'certs' directory is the
# preferred way but may not be supported by the SSL module, thus it has 'ca-bundle.pem'
# as a fallback (which is generated from pem files in the 'certs' directory):
'/etc/ssl/ca-bundle.pem',
]
def get_os_ca_bundle_path():
"""Try to pick an available CA certificate bundle provided by the OS."""
for path in POSSIBLE_CA_BUNDLE_PATHS:
if os.path.exists(path):
return path
return None
# if certifi is installed, use its CA bundle;
# otherwise, try and use the OS bundle
DEFAULT_CA_BUNDLE_PATH = CERTIFI_BUNDLE_PATH or get_os_ca_bundle_path()
DEFAULT_CA_BUNDLE_PATH = certs.where()
def dict_to_sequence(d):
@@ -73,6 +41,15 @@ def dict_to_sequence(d):
return d
def super_len(o):
if hasattr(o, '__len__'):
return len(o)
if hasattr(o, 'len'):
return o.len
if hasattr(o, 'fileno'):
return os.fstat(o.fileno()).st_size
def get_netrc_auth(url):
"""Returns the Requests tuple auth for a given url from netrc."""
@@ -113,7 +90,7 @@ def guess_filename(obj):
"""Tries to guess the filename of the given object."""
name = getattr(obj, 'name', None)
if name and name[0] != '<' and name[-1] != '>':
return name
return os.path.basename(name)
def from_key_val_list(value):
@@ -276,8 +253,7 @@ def add_dict_to_cookiejar(cj, cookie_dict):
"""
cj2 = cookiejar_from_dict(cookie_dict)
for cookie in cj2:
cj.set_cookie(cookie)
cj.update(cj2)
return cj
@@ -371,48 +347,6 @@ def get_unicode_from_response(r):
return r.content
def stream_decompress(iterator, mode='gzip'):
"""Stream decodes an iterator over compressed data
:param iterator: An iterator over compressed data
:param mode: 'gzip' or 'deflate'
:return: An iterator over decompressed data
"""
if mode not in ['gzip', 'deflate']:
raise ValueError('stream_decompress mode must be gzip or deflate')
zlib_mode = 16 + zlib.MAX_WBITS if mode == 'gzip' else -zlib.MAX_WBITS
dec = zlib.decompressobj(zlib_mode)
try:
for chunk in iterator:
rv = dec.decompress(chunk)
if rv:
yield rv
except zlib.error:
# If there was an error decompressing, just return the raw chunk
yield chunk
# Continue to return the rest of the raw data
for chunk in iterator:
yield chunk
else:
# Make sure everything has been returned from the decompression object
buf = dec.decompress(bytes())
rv = buf + dec.flush()
if rv:
yield rv
def stream_untransfer(gen, resp):
ce = resp.headers.get('content-encoding', '').lower()
if 'gzip' in ce:
gen = stream_decompress(gen, mode='gzip')
elif 'deflate' in ce:
gen = stream_decompress(gen, mode='deflate')
return gen
# The unreserved URI characters (RFC 3986)
UNRESERVED_SET = frozenset(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -491,11 +425,9 @@ def default_user_agent():
if _implementation == 'CPython':
_implementation_version = platform.python_version()
elif _implementation == 'PyPy':
_implementation_version = '%s.%s.%s' % (
sys.pypy_version_info.major,
_implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major,
sys.pypy_version_info.minor,
sys.pypy_version_info.micro
)
sys.pypy_version_info.micro)
if sys.pypy_version_info.releaselevel != 'final':
_implementation_version = ''.join([_implementation_version, sys.pypy_version_info.releaselevel])
elif _implementation == 'Jython':
@@ -512,18 +444,17 @@ def default_user_agent():
p_system = 'Unknown'
p_release = 'Unknown'
return " ".join([
'python-requests/%s' % __version__,
'%s/%s' % (_implementation, _implementation_version),
'%s/%s' % (p_system, p_release),
])
return " ".join(['python-requests/%s' % __version__,
'%s/%s' % (_implementation, _implementation_version),
'%s/%s' % (p_system, p_release)])
def default_headers():
return {
return CaseInsensitiveDict({
'User-Agent': default_user_agent(),
'Accept-Encoding': ', '.join(('gzip', 'deflate', 'compress')),
'Accept': '*/*'
}
})
def parse_header_links(value):
@@ -549,7 +480,7 @@ def parse_header_links(value):
for param in params.split(";"):
try:
key,value = param.split("=")
key, value = param.split("=")
except ValueError:
break
@@ -593,3 +524,27 @@ def guess_json_utf(data):
return 'utf-32-le'
# Did not detect a valid UTF-32 ascii-range character
return None
def prepend_scheme_if_needed(url, new_scheme):
'''Given a URL that may or may not have a scheme, prepend the given scheme.
Does not replace a present scheme with the one provided as an argument.'''
scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme)
# urlparse is a finicky beast, and sometimes decides that there isn't a
# netloc present. Assume that it's being over-cautious, and switch netloc
# and path if urlparse decided there was no netloc.
if not netloc:
netloc, path = path, netloc
return urlunparse((scheme, netloc, path, params, query, fragment))
def get_auth_from_url(url):
"""Given a url with authentication components, extract them into a tuple of
username,password."""
if url:
parsed = urlparse(url)
return (parsed.username, parsed.password)
else:
return ('', '')

View File

@@ -1,2 +1,3 @@
pytest
sphinx
py==1.4.12
pytest==2.3.4
invoke==0.2.0

View File

@@ -17,9 +17,10 @@ if sys.argv[-1] == 'publish':
packages = [
'requests',
'requests.packages',
'requests.packages.charade',
'requests.packages.charade',
'requests.packages.urllib3',
'requests.packages.urllib3.packages',
'requests.packages.urllib3.contrib',
'requests.packages.urllib3.packages.ssl_match_hostname'
]
@@ -39,6 +40,7 @@ setup(
package_dir={'requests': 'requests'},
include_package_data=True,
install_requires=requires,
setup_requires=['sphinx'],
license=open('LICENSE').read(),
zip_safe=False,
classifiers=(
@@ -50,9 +52,7 @@ setup(
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
# 'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
),
)

32
tasks.py Normal file
View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import requests
from invoke import run, task
@task
def test():
run('py.test', pty=True)
@task
def deps():
print('Vendoring urllib3...')
run('rm -fr requests/packages/urllib3')
run('git clone https://github.com/shazow/urllib3.git')
run('mv urllib3/urllib3 requests/packages/')
run('rm -fr urllib3')
print('Vendoring Charade...')
run('rm -fr requests/packages/charade')
run('git clone https://github.com/sigmavirus24/charade.git')
run('mv charade/charade requests/packages/')
run('rm -fr charade')
@task
def certs():
print('Grabbing latest CA Bundle...')
r = requests.get('https://raw.github.com/kennethreitz/certifi/master/certifi/cacert.pem')
with open('requests/cacert.pem', 'w') as f:
f.write(r.content)

View File

@@ -3,19 +3,32 @@
"""Tests for Requests."""
from __future__ import division
import json
import os
import unittest
import pickle
import requests
from requests.auth import HTTPDigestAuth
from requests.adapters import HTTPAdapter
from requests.compat import str, cookielib
from requests.cookies import cookiejar_from_dict
from requests.structures import CaseInsensitiveDict
try:
import StringIO
except ImportError:
import io as StringIO
HTTPBIN = os.environ.get('HTTPBIN_URL', 'http://httpbin.org/')
def httpbin(*suffix):
"""Returns url for HTTPBIN resource."""
return HTTPBIN + '/'.join(suffix)
class RequestsTestCase(unittest.TestCase):
_multiprocess_can_split_ = True
@@ -28,9 +41,6 @@ class RequestsTestCase(unittest.TestCase):
"""Teardown."""
pass
def test_assertion(self):
assert 1
def test_entry_points(self):
requests.session
@@ -54,6 +64,11 @@ class RequestsTestCase(unittest.TestCase):
assert pr.url == req.url
assert pr.body == 'life=42'
def test_no_content_length(self):
get_req = requests.Request('GET', httpbin('get')).prepare()
self.assertTrue('Content-Length' not in get_req.headers)
head_req = requests.Request('HEAD', httpbin('head')).prepare()
self.assertTrue('Content-Length' not in head_req.headers)
def test_path_is_not_double_encoded(self):
request = requests.Request('GET', "http://0.0.0.0/get/test case").prepare()
@@ -70,10 +85,6 @@ class RequestsTestCase(unittest.TestCase):
self.assertEqual(request.url,
"http://example.com/path?key=value&a=b#fragment")
def test_HTTP_200_OK_GET(self):
r = requests.get(httpbin('get'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_ALTERNATIVE(self):
r = requests.Request('GET', httpbin('get'))
s = requests.Session()
@@ -86,9 +97,9 @@ class RequestsTestCase(unittest.TestCase):
r = requests.get(httpbin('redirect', '1'))
self.assertEqual(r.status_code, 200)
def test_HTTP_302_ALLOW_REDIRECT_POST(self):
r = requests.post(httpbin('status', '302'), data={'some': 'data'})
self.assertEqual(r.status_code, 200)
# def test_HTTP_302_ALLOW_REDIRECT_POST(self):
# r = requests.post(httpbin('status', '302'), data={'some': 'data'})
# self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
@@ -104,19 +115,61 @@ class RequestsTestCase(unittest.TestCase):
r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_set_cookie_on_301(self):
s = requests.session()
url = httpbin('cookies/set?foo=bar')
r = s.get(url)
self.assertTrue(s.cookies['foo'] == 'bar')
def test_cookie_sent_on_redirect(self):
s = requests.session()
s.get(httpbin('cookies/set?foo=bar'))
r = s.get(httpbin('redirect/1')) # redirects to httpbin('get')
self.assertTrue("Cookie" in r.json()["headers"])
def test_cookie_removed_on_expire(self):
s = requests.session()
s.get(httpbin('cookies/set?foo=bar'))
self.assertTrue(s.cookies['foo'] == 'bar')
s.get(
httpbin('response-headers'),
params={
'Set-Cookie':
'foo=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT'
}
)
assert 'foo' not in s.cookies
def test_request_cookie_overrides_session_cookie(self):
s = requests.session()
s.cookies['foo'] = 'bar'
r = s.get(httpbin('cookies'), cookies={'foo': 'baz'})
assert r.json()['cookies']['foo'] == 'baz'
# Session cookie should not be modified
assert s.cookies['foo'] == 'bar'
def test_generic_cookiejar_works(self):
cj = cookielib.CookieJar()
cookiejar_from_dict({'foo': 'bar'}, cj)
s = requests.session()
s.cookies = cj
r = s.get(httpbin('cookies'))
# Make sure the cookie was sent
assert r.json()['cookies']['foo'] == 'bar'
# Make sure the session cj is still the custom one
assert s.cookies is cj
def test_user_agent_transfers(self):
heads = {
'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
'User-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads)
self.assertTrue(heads['User-agent'] in r.text)
heads = {
'user-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
'user-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads)
@@ -131,8 +184,6 @@ class RequestsTestCase(unittest.TestCase):
self.assertEqual(r.status_code, 200)
def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self):
auth = ('user', 'pass')
url = httpbin('basic-auth', 'user', 'pass')
@@ -163,6 +214,18 @@ class RequestsTestCase(unittest.TestCase):
r = s.get(url)
self.assertEqual(r.status_code, 200)
def test_DIGEST_STREAM(self):
auth = HTTPDigestAuth('user', 'pass')
url = httpbin('digest-auth', 'auth', 'user', 'pass')
r = requests.get(url, auth=auth, stream=True)
self.assertNotEqual(r.raw.read(), b'')
r = requests.get(url, auth=auth, stream=False)
self.assertEqual(r.raw.read(), b'')
def test_DIGESTAUTH_WRONG_HTTP_401_GET(self):
auth = HTTPDigestAuth('user', 'wrongpass')
@@ -242,6 +305,9 @@ class RequestsTestCase(unittest.TestCase):
requests.get(url, params={'foo': 'foo'})
requests.get(httpbin('ø'), params={'foo': 'foo'})
def test_unicode_header_name(self):
requests.put(httpbin('put'), headers={str('Content-Type'): 'application/octet-stream'}, data='\xff') # compat.str is unicode.
def test_urlencoded_get_query_multivalued_param(self):
r = requests.get(httpbin('get'), params=dict(test=['foo', 'baz']))
@@ -255,6 +321,358 @@ class RequestsTestCase(unittest.TestCase):
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
def test_unicode_multipart_post(self):
r = requests.post(httpbin('post'),
data={'stuff': u'ëlïxr'},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
r = requests.post(httpbin('post'),
data={'stuff': u'ëlïxr'.encode('utf-8')},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
r = requests.post(httpbin('post'),
data={'stuff': 'elixr'},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
r = requests.post(httpbin('post'),
data={'stuff': 'elixr'.encode('utf-8')},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
def test_custom_content_type(self):
r = requests.post(httpbin('post'),
data={'stuff': json.dumps({'a': 123})},
files={'file1': ('test_requests.py', open(__file__, 'rb')),
'file2': ('test_requests', open(__file__, 'rb'),
'text/py-content-type')})
self.assertEqual(r.status_code, 200)
self.assertTrue(b"text/py-content-type" in r.request.body)
def test_hook_receives_request_arguments(self):
def hook(resp, **kwargs):
assert resp is not None
assert kwargs != {}
requests.Request('GET', HTTPBIN, hooks={'response': hook})
def test_prepared_request_hook(self):
def hook(resp, **kwargs):
resp.hook_working = True
return resp
req = requests.Request('GET', HTTPBIN, hooks={'response': hook})
prep = req.prepare()
s = requests.Session()
resp = s.send(prep)
self.assertTrue(hasattr(resp, 'hook_working'))
def test_links(self):
r = requests.Response()
r.headers = {
'cache-control': 'public, max-age=60, s-maxage=60',
'connection': 'keep-alive',
'content-encoding': 'gzip',
'content-type': 'application/json; charset=utf-8',
'date': 'Sat, 26 Jan 2013 16:47:56 GMT',
'etag': '"6ff6a73c0e446c1f61614769e3ceb778"',
'last-modified': 'Sat, 26 Jan 2013 16:22:39 GMT',
'link': ('<https://api.github.com/users/kennethreitz/repos?'
'page=2&per_page=10>; rel="next", <https://api.github.'
'com/users/kennethreitz/repos?page=7&per_page=10>; '
' rel="last"'),
'server': 'GitHub.com',
'status': '200 OK',
'vary': 'Accept',
'x-content-type-options': 'nosniff',
'x-github-media-type': 'github.beta',
'x-ratelimit-limit': '60',
'x-ratelimit-remaining': '57'
}
self.assertEqual(r.links['next']['rel'], 'next')
def test_cookie_parameters(self):
key = 'some_cookie'
value = 'some_value'
secure = True
domain = 'test.com'
rest = {'HttpOnly': True}
jar = requests.cookies.RequestsCookieJar()
jar.set(key, value, secure=secure, domain=domain, rest=rest)
self.assertEqual(len(jar), 1)
self.assertTrue('some_cookie' in jar)
cookie = list(jar)[0]
self.assertEqual(cookie.secure, secure)
self.assertEqual(cookie.domain, domain)
self.assertEqual(cookie._rest['HttpOnly'], rest['HttpOnly'])
def test_time_elapsed_blank(self):
r = requests.get(httpbin('get'))
td = r.elapsed
total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600)
* 10**6) / 10**6)
self.assertTrue(total_seconds > 0.0)
def test_response_is_iterable(self):
r = requests.Response()
io = StringIO.StringIO('abc')
read_ = io.read
def read_mock(amt, decode_content=None):
return read_(amt)
setattr(io, 'read', read_mock)
r.raw = io
self.assertTrue(next(iter(r)))
io.close()
def test_get_auth_from_url(self):
url = 'http://user:pass@complex.url.com/path?query=yes'
self.assertEqual(('user', 'pass'),
requests.utils.get_auth_from_url(url))
def test_cannot_send_unprepared_requests(self):
r = requests.Request(url=HTTPBIN)
self.assertRaises(ValueError, requests.Session().send, r)
def test_http_error(self):
error = requests.exceptions.HTTPError()
self.assertEqual(error.response, None)
response = requests.Response()
error = requests.exceptions.HTTPError(response=response)
self.assertEqual(error.response, response)
error = requests.exceptions.HTTPError('message', response=response)
self.assertEqual(str(error), 'message')
self.assertEqual(error.response, response)
def test_session_pickling(self):
r = requests.Request('GET', httpbin('get'))
s = requests.Session()
s = pickle.loads(pickle.dumps(s))
r = s.send(r.prepare())
self.assertEqual(r.status_code, 200)
def test_fixes_1329(self):
"""
Ensure that header updates are done case-insensitively.
"""
s = requests.Session()
s.headers.update({'ACCEPT': 'BOGUS'})
s.headers.update({'accept': 'application/json'})
r = s.get(httpbin('get'))
headers = r.request.headers
# ASCII encode because of key comparison changes in py3
self.assertEqual(
headers['accept'.encode('ascii')],
'application/json'
)
self.assertEqual(
headers['Accept'.encode('ascii')],
'application/json'
)
self.assertEqual(
headers['ACCEPT'.encode('ascii')],
'application/json'
)
def test_transport_adapter_ordering(self):
s = requests.Session()
order = ['https://', 'http://']
self.assertEqual(order, list(s.adapters))
s.mount('http://git', HTTPAdapter())
s.mount('http://github', HTTPAdapter())
s.mount('http://github.com', HTTPAdapter())
s.mount('http://github.com/about/', HTTPAdapter())
order = [
'http://github.com/about/',
'http://github.com',
'http://github',
'http://git',
'https://',
'http://',
]
self.assertEqual(order, list(s.adapters))
s.mount('http://gittip', HTTPAdapter())
s.mount('http://gittip.com', HTTPAdapter())
s.mount('http://gittip.com/about/', HTTPAdapter())
order = [
'http://github.com/about/',
'http://gittip.com/about/',
'http://github.com',
'http://gittip.com',
'http://github',
'http://gittip',
'http://git',
'https://',
'http://',
]
self.assertEqual(order, list(s.adapters))
s2 = requests.Session()
s2.adapters = {'http://': HTTPAdapter()}
s2.mount('https://', HTTPAdapter())
self.assertTrue('http://' in s2.adapters)
self.assertTrue('https://' in s2.adapters)
def test_long_authinfo_in_url(self):
url = 'http://{0}:{1}@{2}:9000/path?query#frag'.format(
'E8A3BE87-9E3F-4620-8858-95478E385B5B',
'EA770032-DA4D-4D84-8CE9-29C6D910BF1E',
'exactly-------------sixty-----------three------------characters',
)
r = requests.Request('GET', url).prepare()
self.assertEqual(r.url, url)
class TestCaseInsensitiveDict(unittest.TestCase):
def test_mapping_init(self):
cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
def test_iterable_init(self):
cid = CaseInsensitiveDict([('Foo', 'foo'), ('BAr', 'bar')])
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
def test_kwargs_init(self):
cid = CaseInsensitiveDict(FOO='foo', BAr='bar')
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
def test_docstring_example(self):
cid = CaseInsensitiveDict()
cid['Accept'] = 'application/json'
self.assertEqual(cid['aCCEPT'], 'application/json')
self.assertEqual(list(cid), ['Accept'])
def test_len(self):
cid = CaseInsensitiveDict({'a': 'a', 'b': 'b'})
cid['A'] = 'a'
self.assertEqual(len(cid), 2)
def test_getitem(self):
cid = CaseInsensitiveDict({'Spam': 'blueval'})
self.assertEqual(cid['spam'], 'blueval')
self.assertEqual(cid['SPAM'], 'blueval')
def test_fixes_649(self):
"""__setitem__ should behave case-insensitively."""
cid = CaseInsensitiveDict()
cid['spam'] = 'oneval'
cid['Spam'] = 'twoval'
cid['sPAM'] = 'redval'
cid['SPAM'] = 'blueval'
self.assertEqual(cid['spam'], 'blueval')
self.assertEqual(cid['SPAM'], 'blueval')
self.assertEqual(list(cid.keys()), ['SPAM'])
def test_delitem(self):
cid = CaseInsensitiveDict()
cid['Spam'] = 'someval'
del cid['sPam']
self.assertFalse('spam' in cid)
self.assertEqual(len(cid), 0)
def test_contains(self):
cid = CaseInsensitiveDict()
cid['Spam'] = 'someval'
self.assertTrue('Spam' in cid)
self.assertTrue('spam' in cid)
self.assertTrue('SPAM' in cid)
self.assertTrue('sPam' in cid)
self.assertFalse('notspam' in cid)
def test_get(self):
cid = CaseInsensitiveDict()
cid['spam'] = 'oneval'
cid['SPAM'] = 'blueval'
self.assertEqual(cid.get('spam'), 'blueval')
self.assertEqual(cid.get('SPAM'), 'blueval')
self.assertEqual(cid.get('sPam'), 'blueval')
self.assertEqual(cid.get('notspam', 'default'), 'default')
def test_update(self):
cid = CaseInsensitiveDict()
cid['spam'] = 'blueval'
cid.update({'sPam': 'notblueval'})
self.assertEqual(cid['spam'], 'notblueval')
cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
cid.update({'fOO': 'anotherfoo', 'bAR': 'anotherbar'})
self.assertEqual(len(cid), 2)
self.assertEqual(cid['foo'], 'anotherfoo')
self.assertEqual(cid['bar'], 'anotherbar')
def test_update_retains_unchanged(self):
cid = CaseInsensitiveDict({'foo': 'foo', 'bar': 'bar'})
cid.update({'foo': 'newfoo'})
self.assertEquals(cid['bar'], 'bar')
def test_iter(self):
cid = CaseInsensitiveDict({'Spam': 'spam', 'Eggs': 'eggs'})
keys = frozenset(['Spam', 'Eggs'])
self.assertEqual(frozenset(iter(cid)), keys)
def test_equality(self):
cid = CaseInsensitiveDict({'SPAM': 'blueval', 'Eggs': 'redval'})
othercid = CaseInsensitiveDict({'spam': 'blueval', 'eggs': 'redval'})
self.assertEqual(cid, othercid)
del othercid['spam']
self.assertNotEqual(cid, othercid)
self.assertEqual(cid, {'spam': 'blueval', 'eggs': 'redval'})
def test_setdefault(self):
cid = CaseInsensitiveDict({'Spam': 'blueval'})
self.assertEqual(
cid.setdefault('spam', 'notblueval'),
'blueval'
)
self.assertEqual(
cid.setdefault('notspam', 'notblueval'),
'notblueval'
)
def test_lower_items(self):
cid = CaseInsensitiveDict({
'Accept': 'application/json',
'user-Agent': 'requests',
})
keyset = frozenset(lowerkey for lowerkey, v in cid.lower_items())
lowerkeyset = frozenset(['accept', 'user-agent'])
self.assertEqual(keyset, lowerkeyset)
def test_preserve_key_case(self):
cid = CaseInsensitiveDict({
'Accept': 'application/json',
'user-Agent': 'requests',
})
keyset = frozenset(['Accept', 'user-Agent'])
self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
self.assertEqual(frozenset(cid.keys()), keyset)
self.assertEqual(frozenset(cid), keyset)
def test_preserve_last_key_case(self):
cid = CaseInsensitiveDict({
'Accept': 'application/json',
'user-Agent': 'requests',
})
cid.update({'ACCEPT': 'application/json'})
cid['USER-AGENT'] = 'requests'
keyset = frozenset(['ACCEPT', 'USER-AGENT'])
self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
self.assertEqual(frozenset(cid.keys()), keyset)
self.assertEqual(frozenset(cid), keyset)
if __name__ == '__main__':