diff --git a/.gitignore b/.gitignore index b2abf8c2..19ebfd79 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ nosetests.xml junit-report.xml pylint.txt toy.py +.cache/ cover/ build/ docs/_build diff --git a/.travis.yml b/.travis.yml index 20075db2..9f2397d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ sudo: false language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" diff --git a/AUTHORS.rst b/AUTHORS.rst old mode 100755 new mode 100644 index 832b8fd4..65123510 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -10,13 +10,6 @@ Keepers of the Four Crystals - Nate Prewitt `@nateprewitt `_ - -Urllib3 -``````` - -- Andrey Petrov - - Patches and Suggestions ``````````````````````` @@ -186,3 +179,6 @@ Patches and Suggestions - Shmuel Amar (`@shmuelamar `_) - Gary Wu (`@garywu `_) - Ryan Pineo (`@ryanpineo `_) +- Ed Morley (`@edmorley `_) +- Matt Liu (`@mlcrazy `_) +- Taylor Hoff (`@PrimordialHelios `_) diff --git a/HISTORY.rst b/HISTORY.rst index 7cff0cae..efb9b6f2 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,10 +6,70 @@ Release History dev +++ +**Improvements** + +- Warn user about possible slowdown when using cryptography version < 1.3.4 + +**Bugfixes** + +- Parsing empty ``Link`` headers with ``parse_header_links()`` no longer return one bogus entry + +2.18.4 (2017-08-15) ++++++++++++++++++++ + +**Improvements** + +- Error messages for invalid headers now include the header name for easier debugging + +**Dependencies** + +- We now support idna v2.6. + +2.18.3 (2017-08-02) ++++++++++++++++++++ + +**Improvements** + +- Running ``$ python -m requests.help`` now includes the installed version of idna. + +**Bugfixes** + +- Fixed issue where Requests would raise ``ConnectionError`` instead of + ``SSLError`` when encountering SSL problems when using urllib3 v1.22. + +2.18.2 (2017-07-25) ++++++++++++++++++++ + +**Bugfixes** + +- ``requests.help`` no longer fails on Python 2.6 due to the absence of + ``ssl.OPENSSL_VERSION_NUMBER``. + +**Dependencies** + +- We now support urllib3 v1.22. + +2.18.1 (2017-06-14) ++++++++++++++++++++ + +**Bugfixes** + +- Fix an error in the packaging whereby the ``*.whl`` contained incorrect data + that regressed the fix in v2.17.3. + +2.18.0 (2017-06-14) ++++++++++++++++++++ + +**Improvements** + +- ``Response`` is now a context manager, so can be used directly in a ``with`` statement + without first having to be wrapped by ``contextlib.closing()``. + **Bugfixes** - Resolve installation failure if multiprocessing is not available - Resolve tests crash if multiprocessing is not able to determine the number of CPU cores +- Resolve error swallowing in utils set_environ generator 2.17.3 (2017-05-29) @@ -1481,4 +1541,3 @@ This is not a backwards compatible change. * Frustration * Conception - diff --git a/MANIFEST.in b/MANIFEST.in index 650744ca..2c0fb95c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include README.rst LICENSE NOTICE HISTORY.rst pytest.ini +include README.rst LICENSE NOTICE HISTORY.rst pytest.ini requirements.txt recursive-include tests *.py diff --git a/Makefile b/Makefile index f90248f7..317a7c76 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,21 @@ .PHONY: docs init: - pip install -r requirements.txt + pip install pipenv --upgrade + pipenv install --dev --skip-lock test: # This runs all of the tests, on both Python 2 and Python 3. detox ci: - py.test -n 8 --boxed --junitxml=report.xml + pipenv run py.test -n 8 --boxed --junitxml=report.xml test-readme: - python setup.py check -r -s + @pipenv run python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo "README.rst and HISTORY.rst ok") || echo "Invalid markup in README.rst or HISTORY.rst!" flake8: - flake8 --ignore=E501,F401,E128,E402,E731,F821 requests + pipenv run flake8 --ignore=E501,F401,E128,E402,E731,F821 requests coverage: - py.test --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=requests tests + pipenv run py.test --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=requests tests publish: pip install 'twine>=1.5.0' diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..716c0058 --- /dev/null +++ b/Pipfile @@ -0,0 +1,24 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true + +[dev-packages] + +pytest = ">=2.8.0" +codecov = "*" +"pytest-httpbin" = "==0.0.7" +"pytest-mock" = "*" +"pytest-cov" = "*" +"pytest-xdist" = "*" +alabaster = "*" +"readme-renderer" = "*" +sphinx = "<=1.5.5" +pysocks = "*" +docutils = "*" +"flake8" = "*" +tox = "*" +detox = "*" +httpbin = "==0.5.0" + +[packages] +"e1839a8" = {path = ".", editable = true, extras=["socks"]} \ No newline at end of file diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 00000000..fd9a1a10 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,517 @@ +{ + "_meta": { + "hash": { + "sha256": "72b5a08e9c266b930d308024036e928e6b99ed4b7a50f22af377a463b7867a14" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.2", + "os_name": "posix", + "platform_machine": "x86_64", + "platform_python_implementation": "CPython", + "platform_release": "16.7.0", + "platform_system": "Darwin", + "platform_version": "Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64", + "python_full_version": "3.6.2", + "python_version": "3.6", + "sys_platform": "darwin" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:54a07c09c586b0e4c619f02a5e94e36619da8e2b053e20f594348c0611803704", + "sha256:40523d2efb60523e113b44602298f0960e900388cf3bb6043f645cf57ea9e3f5" + ], + "version": "==2017.7.27.1" + }, + "chardet": { + "hashes": [ + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + ], + "version": "==3.0.4" + }, + "e1839a8": { + "editable": true, + "extras": [ + "socks" + ], + "path": "." + }, + "idna": { + "hashes": [ + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + ], + "version": "==2.6" + }, + "pysocks": { + "hashes": [ + "sha256:18842328a4e6061f084cfba70f6950d9140ecf7418b3df7cef558ebb217bac8d", + "sha256:d00329f27efa157db7efe3ca26fcd69033cd61f83822461ee3f8a353b48e33cf" + ], + "version": "==1.6.7" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + } + }, + "develop": { + "alabaster": { + "hashes": [ + "sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732", + "sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0" + ], + "version": "==0.7.10" + }, + "apipkg": { + "hashes": [ + "sha256:65d2aa68b28e7d31233bb2ba8eb31cda40e4671f8ac2d6b241e358c9652a74b9", + "sha256:2e38399dbe842891fe85392601aab8f40a8f4cc5a9053c326de35a1cc0297ac6" + ], + "version": "==1.4" + }, + "babel": { + "hashes": [ + "sha256:f20b2acd44f587988ff185d8949c3e208b4b3d5d20fcab7d91fe481ffa435528", + "sha256:6007daf714d0cd5524bbe436e2d42b3c20e68da66289559341e48d2cd6d25811" + ], + "version": "==2.5.1" + }, + "bleach": { + "hashes": [ + "sha256:a6d9d5f5b7368c1689ad7f128af8e792beea23393688872b576c0271e6564a16", + "sha256:b9522130003e4caedf4f00a39c120a906dcd4242329c1c8f621f5370203cbc30" + ], + "version": "==2.0.0" + }, + "certifi": { + "hashes": [ + "sha256:54a07c09c586b0e4c619f02a5e94e36619da8e2b053e20f594348c0611803704", + "sha256:40523d2efb60523e113b44602298f0960e900388cf3bb6043f645cf57ea9e3f5" + ], + "version": "==2017.7.27.1" + }, + "chardet": { + "hashes": [ + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "version": "==6.7" + }, + "codecov": { + "hashes": [ + "sha256:ad82f054837b02081f86ed1eb6c04cddc029fbc734eaf92ff73da1db3a79188b", + "sha256:db1c182ca896244d8644d8410a33f6f6dd1cc24d80209907a65077445923f00c" + ], + "version": "==2.0.9" + }, + "configparser": { + "hashes": [ + "sha256:5308b47021bc2340965c371f0f058cc6971a04502638d4244225c49d80db273a" + ], + "version": "==3.5.0" + }, + "coverage": { + "hashes": [ + "sha256:c1456f66c536010cf9e4633a8853a9153e8fd588393695295afd4d0fc16c1d74", + "sha256:97a7ec51cdde3a386e390b159b20f247ccb478084d925c75f1faa3d26c01335e", + "sha256:83e955b975666b5a07d217135e7797857ce844eb340a99e46cc25525120417c4", + "sha256:483ed14080c5301048128bb027b77978c632dd9e92e3ecb09b7e28f5b92abfcf", + "sha256:ef574ab9640bcfa2f3c671831faf03f65788945fdf8efa4d4a1fffc034838e2a", + "sha256:c5a205b4da3c624f5119dc4d84240789b5906bb8468902ec22dcc4aad8aa4638", + "sha256:5dea90ed140e7fa9bc00463313f9bc4a6e6aff297b4969615e7a688615c4c4d2", + "sha256:f9e83b39d29c2815a38e4118d776b482d4082b5bf9c9147fbc99a3f83abe480a", + "sha256:700040c354f0230287906b1276635552a3def4b646e0145555bc9e2e5da9e365", + "sha256:7f1eacae700c66c3d7362a433b228599c9d94a5a3a52613dddd9474e04deb6bc", + "sha256:13ef9f799c8fb45c446a239df68034de3a6f3de274881b088bebd7f5661f79f8", + "sha256:dfb011587e2b7299112f08a2a60d2601706aac9abde37aa1177ea825adaed923", + "sha256:381be5d31d3f0d912334cf2c159bc7bea6bfe6b0e3df6061a3bf2bf88359b1f6", + "sha256:83a477ac4f55a6ef59552683a0544d47b68a85ce6a80fd0ca6b3dc767f6495fb", + "sha256:dfd35f1979da31bcabbe27bcf78d4284d69870731874af629082590023a77336", + "sha256:9681efc2d310cfc53863cc6f63e88ebe7a48124550fa822147996cb09390b6ab", + "sha256:53770b20ac5b4a12e99229d4bae57af0945be87cc257fce6c6c7571a39f0c5d4", + "sha256:8801880d32f11b6df11c32a961e186774b4634ae39d7c43235f5a24368a85f07", + "sha256:16db2c69a1acbcb3c13211e9f954e22b22a729909d81f983b6b9badacc466eda", + "sha256:ef43a06a960b46c73c018704051e023ee6082030f145841ffafc8728039d5a88", + "sha256:c3e2736664a6074fc9bd54fb643f5af0fc60bfedb2963b3d3f98c7450335e34c", + "sha256:17709e22e4c9f5412ba90f446fb13b245cc20bf4a60377021bbff6c0f1f63e7c", + "sha256:a2f7106d1167825c4115794c2ba57cc3b15feb6183db5328fa66f94c12902d8b", + "sha256:2a08e978f402696c6956eee9d1b7e95d3ad042959b71bafe1f3e4557cbd6e0ac", + "sha256:57f510bb16efaec0b6f371b64a8000c62e7e3b3e48e8b0a5745ade078d849814", + "sha256:0f1883eab9c19aa243f51308751b8a2a547b9b817b721cc0ecf3efb99fafbea7", + "sha256:e00fe141e22ce6e9395aa24d862039eb180c6b7e89df0bbaf9765e9aebe560a9", + "sha256:ec596e4401553caa6dd2e3349ce47f9ef82c1f1bcba5d8ac3342724f0df8d6ff", + "sha256:c820a533a943ebc860acc0ce6a00dd36e0fdf2c6f619ff8225755169428c5fa2", + "sha256:b7f7283eb7badd2b8a9c6a9d6eeca200a0a24db6be79baee2c11398f978edcaa", + "sha256:a5ed27ad3e8420b2d6b625dcbd3e59488c14ccc06030167bcf14ffb0f4189b77", + "sha256:d7b70b7b4eb14d0753d33253fe4f121ca99102612e2719f0993607deb30c6f33", + "sha256:4047dc83773869701bde934fb3c4792648eda7c0e008a77a0aec64157d246801", + "sha256:7a9c44400ee0f3b4546066e0710e1250fd75831adc02ab99dda176ad8726f424", + "sha256:0f649e68db74b1b5b8ca4161d08eb2b8fa8ae11af1ebfb80e80e112eb0ef5300", + "sha256:52964fae0fafef8bd283ad8e9a9665205a9fdf912535434defc0ec3def1da26b", + "sha256:36aa6c8db83bc27346ddcd8c2a60846a7178ecd702672689d3ea1828eb1a4d11", + "sha256:9824e15b387d331c0fc0fef905a539ab69784368a1d6ac3db864b4182e520948", + "sha256:4a678e1b9619a29c51301af61ab84122e2f8cc7a0a6b40854b808ac6be604300", + "sha256:8bb7c8dca54109b61013bc4114d96effbf10dea136722c586bce3a5d9fc4e730", + "sha256:1a41d621aa9b6ab6457b557a754d50aaff0813fad3453434de075496fca8a183", + "sha256:0fa423599fc3d9e18177f913552cdb34a8d9ad33efcf52a98c9d4b644edb42c5", + "sha256:e61a4ba0b2686040cb4828297c7e37bcaf3a1a1c0bc0dbe46cc789dde51a80fa", + "sha256:ce9ef0fc99d11d418662e36fd8de6d71b19ec87c2eab961a117cc9d087576e72" + ], + "version": "==4.4.1" + }, + "decorator": { + "hashes": [ + "sha256:95a26b17806e284452bfd97fa20aa1e8cb4ee23542bda4dbac5e4562aa1642cd", + "sha256:7cb64d38cb8002971710c8899fbdfb859a23a364b7c99dab19d1f719c2ba16b5" + ], + "version": "==4.1.2" + }, + "detox": { + "hashes": [ + "sha256:af0097ea01263f68f546826df69b9301458d6cec0ed278c53c01f9529fbd349e", + "sha256:4719ca48c4ea5ffd908b1bc3d5d1b593b41e71dee17180d58d8a3e7e8f588d45" + ], + "version": "==0.11" + }, + "docutils": { + "hashes": [ + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6", + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274" + ], + "version": "==0.14" + }, + "enum-compat": { + "hashes": [ + "sha256:939ceff18186a5762ae4db9fa7bfe017edbd03b66526b798dd8245394c8a4192" + ], + "version": "==0.0.2" + }, + "enum34": { + "hashes": [ + "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", + "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", + "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1", + "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850" + ], + "version": "==1.1.6" + }, + "eventlet": { + "hashes": [ + "sha256:0a7d1e1d2f4dd2e0b2cb627dadf7a0f23de0eca88ba2d6af4229abe32a24dec9", + "sha256:08faffab88c1b08bd53ea28bf084a572c89f7e7648bd9d71e6116ac17a51a15d" + ], + "version": "==0.21.0" + }, + "execnet": { + "hashes": [ + "sha256:d2b909c7945832e1c19cfacd96e78da68bdadc656440cfc7dfe59b766744eb8c", + "sha256:f66dd4a7519725a1b7e14ad9ae7d3df8e09b2da88062386e08e941cafc0ef3e6" + ], + "version": "==1.4.1" + }, + "flake8": { + "hashes": [ + "sha256:f1a9d8886a9cbefb52485f4f4c770832c7fb569c084a9a314fb1eaa37c0c2c86", + "sha256:c20044779ff848f67f89c56a0e4624c04298cd476e25253ac0c36f910a1a11d8" + ], + "version": "==3.4.1" + }, + "flask": { + "hashes": [ + "sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856", + "sha256:49f44461237b69ecd901cc7ce66feea0319b9158743dd27a2899962ab214dac1" + ], + "version": "==0.12.2" + }, + "funcsigs": { + "hashes": [ + "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", + "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50" + ], + "version": "==1.0.2" + }, + "greenlet": { + "hashes": [ + "sha256:96888e47898a471073b394ea641b7d675c1d054c580dd4a04a382bd34e67d89e", + "sha256:d2d5103f6cba131e1be660230018e21f276911d2b68b629ead1c5cb5e5472ac7", + "sha256:bc339de0e0969de5118d0b62a080a7611e2ba729a90f4a3ad78559c51bc5576d", + "sha256:b8ab98f8ae25938326dc4c21e3689a933531500ae4f3bfcefe36e3e25fda4dbf", + "sha256:416a3328d7e0a19aa1df3ec09524a109061fd7b80e010ef0dff9f695b4ac5e20", + "sha256:21232907c8c26838b16915bd8fbbf82fc70c996073464cc70981dd4a96bc841c", + "sha256:6803d8c6b235c861c50afddf00c7467ffbcd5ab960d137ff0f9c36f2cb11ee4b", + "sha256:76dab055476dd4dabb00a967b4df1990b25542d17eaa40a18f66971d10193e0b", + "sha256:70b9ff28921f5a3c03df4896ec8c55f5f94c593d7a79abd98b4c5c4a692ba873", + "sha256:7114b757b4146f4c87a0f00f1e58abd4c4729836679af0fc37266910a4a72eb0", + "sha256:0d90c709355ed13f16676f84e5a9cd67826a9f5c5143381c21e8fc3100ade1f1", + "sha256:ebae83b6247f83b1e8d887733dfa8046ce6e29d8b3e2a7380256e9de5c6ae55d", + "sha256:e841e3ece633acae5e2bf6102140a605ffee7d5d4921dca1625c5fdc0f0b3248", + "sha256:3e5e9be157ece49e4f97f3225460caf758ccb00f934fcbc5db34367cc1ff0aee", + "sha256:e77b708c37b652c7501b9f8f6056b23633c567aaa0d29edfef1c11673c64b949", + "sha256:0da1fc809c3bdb93fbacd0f921f461aacd53e554a7b7d4e9953ba09131c4206e", + "sha256:66fa5b101fcf4521138c1a29668074268d938bbb7de739c8faa9f92ea1f05e1f", + "sha256:e5451e1ce06b74a4861576c2db74405a4398c4809a105774550a9e52cfc8c4da", + "sha256:9c407aa6adfd4eea1232e81aa9f3cb3d9b955a9891c4819bf9b498c77efba14b", + "sha256:b56ac981f07b77e72ad5154278b93396d706572ea52c2fce79fee2abfcc8bfa6", + "sha256:e4c99c6010a5d153d481fdaf63b8a0782825c0721506d880403a3b9b82ae347e" + ], + "version": "==0.4.12" + }, + "html5lib": { + "hashes": [ + "sha256:b8934484cf22f1db684c0fae27569a0db404d0208d20163fbf51cc537245d008", + "sha256:ee747c0ffd3028d2722061936b5c65ee4fe13c8e4613519b4447123fc4546298" + ], + "version": "==0.999999999" + }, + "httpbin": { + "hashes": [ + "sha256:710069973216d4bbf9ab6757f1e9a1f3be05832ce77da023adce0a98dfeecfee", + "sha256:79fbc5d27e4194ea908b0fa18e09a59d95d287c91667aa69bcd010342d1589b5" + ], + "version": "==0.5.0" + }, + "idna": { + "hashes": [ + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + ], + "version": "==2.6" + }, + "imagesize": { + "hashes": [ + "sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb", + "sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062" + ], + "version": "==0.7.1" + }, + "itsdangerous": { + "hashes": [ + "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" + ], + "version": "==0.24" + }, + "jinja2": { + "hashes": [ + "sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054", + "sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff" + ], + "version": "==2.9.6" + }, + "markupsafe": { + "hashes": [ + "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + ], + "version": "==1.0" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "mock": { + "hashes": [ + "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", + "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba" + ], + "version": "==2.0.0" + }, + "pbr": { + "hashes": [ + "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac", + "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1" + ], + "version": "==3.1.1" + }, + "pluggy": { + "hashes": [ + "sha256:bd60171dbb250fdebafad46ed16d97065369da40568ae948ef7117eee8536e94" + ], + "version": "==0.5.2" + }, + "py": { + "hashes": [ + "sha256:2ccb79b01769d99115aa600d7eed99f524bf752bba8f041dc1c184853514655a", + "sha256:0f2d585d22050e90c7d293b6451c83db097df77871974d90efd5a30dc12fcde3" + ], + "version": "==1.4.34" + }, + "pycodestyle": { + "hashes": [ + "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9", + "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766" + ], + "version": "==2.3.1" + }, + "pyflakes": { + "hashes": [ + "sha256:cc5eadfb38041f8366128786b4ca12700ed05bbf1403d808e89d57d67a3875a7", + "sha256:aa0d4dff45c0cc2214ba158d29280f8fa1129f3e87858ef825930845146337f4" + ], + "version": "==1.5.0" + }, + "pygments": { + "hashes": [ + "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", + "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" + ], + "version": "==2.2.0" + }, + "pysocks": { + "hashes": [ + "sha256:18842328a4e6061f084cfba70f6950d9140ecf7418b3df7cef558ebb217bac8d", + "sha256:d00329f27efa157db7efe3ca26fcd69033cd61f83822461ee3f8a353b48e33cf" + ], + "version": "==1.6.7" + }, + "pytest": { + "hashes": [ + "sha256:b84f554f8ddc23add65c411bf112b2d88e2489fd45f753b1cae5936358bdf314", + "sha256:f46e49e0340a532764991c498244a60e3a37d7424a532b3ff1a6a7653f1a403a" + ], + "version": "==3.2.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec", + "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d" + ], + "version": "==2.5.1" + }, + "pytest-forked": { + "hashes": [ + "sha256:f275cb48a73fc61a6710726348e1da6d68a978f0ec0c54ece5a5fae5977e5a08", + "sha256:e4500cd0509ec4a26535f7d4112a8cc0f17d3a41c29ffd4eab479d2a55b30805" + ], + "version": "==0.2" + }, + "pytest-httpbin": { + "hashes": [ + "sha256:f430f0b5742a9d325148a3428f890f538f331cb7b244a49873cc322f838c85ea", + "sha256:03af8a7055c8bbcb68b14d9a14c103c82c97aeb86a8f1b29cd63d83644c2f021" + ], + "version": "==0.0.7" + }, + "pytest-mock": { + "hashes": [ + "sha256:7ed6e7e8c636fd320927c5d73aedb77ac2eeb37196c3410e6176b7c92fdf2f69", + "sha256:920d1167af5c2c2ad3fa0717d0c6c52e97e97810160c15721ac895cac53abb1c" + ], + "version": "==1.6.3" + }, + "pytest-xdist": { + "hashes": [ + "sha256:7924d45c2430191fe3679a58116c74ceea13307d7822c169d65fd59a24b3a4fe" + ], + "version": "==1.20.0" + }, + "pytz": { + "hashes": [ + "sha256:c883c2d6670042c7bc1688645cac73dd2b03193d1f7a6847b6154e96890be06d", + "sha256:03c9962afe00e503e2d96abab4e8998a0f84d4230fa57afe1e0528473698cdd9", + "sha256:487e7d50710661116325747a9cd1744d3323f8e49748e287bc9e659060ec6bf9", + "sha256:43f52d4c6a0be301d53ebd867de05e2926c35728b3260157d274635a0a947f1c", + "sha256:d1d6729c85acea5423671382868627129432fba9a89ecbb248d8d1c7a9f01c67", + "sha256:54a935085f7bf101f86b2aff75bd9672b435f51c3339db2ff616e66845f2b8f9", + "sha256:39504670abb5dae77f56f8eb63823937ce727d7cdd0088e6909e6dcac0f89043", + "sha256:ddc93b6d41cfb81266a27d23a79e13805d4a5521032b512643af8729041a81b4", + "sha256:f5c056e8f62d45ba8215e5cb8f50dfccb198b4b9fbea8500674f3443e4689589" + ], + "version": "==2017.2" + }, + "readme-renderer": { + "hashes": [ + "sha256:c9637bfcf1ff40f7683b3439f4b97eb0f9a1cffc2a1fad5fa01debd667ddb111", + "sha256:9deab442963a63a71ab494bf581b1c844473995a2357f4b3228a1df1c8cba8da" + ], + "version": "==17.2" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "version": "==2.18.4" + }, + "six": { + "hashes": [ + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + ], + "version": "==1.11.0" + }, + "snowballstemmer": { + "hashes": [ + "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89", + "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128" + ], + "version": "==1.2.1" + }, + "sphinx": { + "hashes": [ + "sha256:11f271e7a9398385ed730e90f0bb41dc3815294bdcd395b46ed2d033bc2e7d87", + "sha256:4064ea6c56feeb268838cb8fbbee507d0c3d5d92fa63a7df935a916b52c9e2f5" + ], + "version": "==1.5.5" + }, + "tox": { + "hashes": [ + "sha256:49d88f2c217352c499450e9f61ca82fd9c8873d01a45555bb342a32f2b6753a2", + "sha256:d9c279e707d2cfef8d77d10f13b38b3e68b7e470018b45747560f6c3c66d6b83" + ], + "version": "==2.8.2" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + }, + "virtualenv": { + "hashes": [ + "sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0", + "sha256:02f8102c2436bb03b3ee6dede1919d1dac8a427541652e5ec95171ec8adbc93a" + ], + "version": "==15.1.0" + }, + "webencodings": { + "hashes": [ + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" + ], + "version": "==0.5.1" + }, + "werkzeug": { + "hashes": [ + "sha256:e8549c143af3ce6559699a01e26fa4174f4c591dbee0a499f3cd4c3781cdec3d", + "sha256:903a7b87b74635244548b30d30db4c8947fe64c5198f58899ddcd3a13c23bb26" + ], + "version": "==0.12.2" + } + } +} diff --git a/README.rst b/README.rst index 13fafe4c..24d3a671 100644 --- a/README.rst +++ b/README.rst @@ -20,14 +20,10 @@ Requests: HTTP for Humans .. image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg :target: https://saythanks.io/to/kennethreitz - - Requests is the only *Non-GMO* HTTP library for Python, safe for human consumption. -**Warning:** Recreational use of the Python standard library for HTTP may result in dangerous side-effects, -including: security vulnerabilities, verbose code, reinventing the wheel, -constantly reading documentation, depression, headaches, or even death. +.. image:: https://farm5.staticflickr.com/4317/35198386374_1939af3de6_k_d.jpg Behold, the power of Requests: @@ -81,19 +77,19 @@ Requests is ready for today's web. - ``.netrc`` Support - Chunked Requests -Requests officially supports Python 2.7 & 3.3–3.7, and runs great on PyPy. +Requests officially supports Python 2.7 & 3.4–3.7, and runs great on PyPy. Installation ------------ -To install Requests, simply: +To install Requests, simply use `pipenv `_ (or pip, of course): .. code-block:: bash - $ pip install requests + $ pipenv install requests ✨🍰✨ -Satisfaction, guaranteed. +Satisfaction guaranteed. Documentation ------------- diff --git a/appveyor.yml b/appveyor.yml index d2d6b6bb..1da1fa88 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,11 +10,6 @@ environment: PYTHON_ARCH: "64" TOXENV: "py27" - - PYTHON: "C:\\Python33-x64" - PYTHON_VERSION: "3.3.x" - PYTHON_ARCH: "64" - TOXENV: "py33" - - PYTHON: "C:\\Python34-x64" PYTHON_VERSION: "3.4.x" PYTHON_ARCH: "64" @@ -53,4 +48,4 @@ test_script: - "C:\\MinGW\\bin\\mingw32-make coverage" on_success: - - "codecov -f coverage.xml" + - "pipenv run codecov -f coverage.xml" diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000..3a8af312 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,39 @@ +#carbonads { + display: block; + overflow: hidden; + padding: 1em; + background-color: #eeeeee; + text-align: center; + border: solid 1px #cccccc; + margin: 1.5em 0 2em; + border-radius: 2px; + line-height: 1.5; +} + +#carbonads a { + border-bottom: 0; +} + +#carbonads span { + display: block; + overflow: hidden; +} + +.carbon-img { + display: block; + margin: 0 auto 1em; + text-align: center; +} + +.carbon-text { + display: block; + margin-bottom: 1em; +} + +.carbon-poweredby { + display: block; + text-transform: uppercase; + letter-spacing: 1px; + line-height: 1; + font-size: 10px; +} diff --git a/docs/_templates/hacks.html b/docs/_templates/hacks.html index f9fc96cb..0d88a6ec 100644 --- a/docs/_templates/hacks.html +++ b/docs/_templates/hacks.html @@ -1,4 +1,34 @@ + + + + + + + + + diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index 9f758dcc..5b437d85 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -14,17 +14,9 @@ human beings.

+ -

The Hitchhiker's Guide to Python

- -

This guide is now available in tangible book form!

- - - -

All proceeds are being directly donated to the DjangoGirls organization.

- - - +

Stickers!

Stay Informed

Receive updates on new releases and upcoming projects.

@@ -34,8 +26,6 @@

Say Thanks!

Join Mailing List.

- -

Other Projects

More Kenneth Reitz projects:

diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html index d30c0c15..b31c3477 100644 --- a/docs/_templates/sidebarlogo.html +++ b/docs/_templates/sidebarlogo.html @@ -13,7 +13,7 @@ human beings. You are currently looking at the documentation of the development release.

- +

Stickers!

Stay Informed

Receive updates on new releases and upcoming projects.

@@ -21,14 +21,7 @@
-

The Hitchhiker's Guide to Python

- -

This guide is now available in tangible book form!

- - - -

All proceeds are being directly donated to the DjangoGirls organization.

- +

If you enjoy using this project, Say Thanks!

diff --git a/docs/community/faq.rst b/docs/community/faq.rst index 256856ad..e1c8e9ff 100644 --- a/docs/community/faq.rst +++ b/docs/community/faq.rst @@ -3,6 +3,8 @@ Frequently Asked Questions ========================== +.. image:: https://farm5.staticflickr.com/4290/35294660055_42c02b2316_k_d.jpg + This part of the documentation answers common questions about Requests. Encoded Data? @@ -55,7 +57,6 @@ Yes! Here's a list of Python platforms that are officially supported: * Python 2.7 -* Python 3.3 * Python 3.4 * Python 3.5 * Python 3.6 diff --git a/docs/community/out-there.rst b/docs/community/out-there.rst index 645c0ac4..5ce5f79f 100644 --- a/docs/community/out-there.rst +++ b/docs/community/out-there.rst @@ -1,6 +1,8 @@ Integrations ============ +.. image:: https://farm5.staticflickr.com/4239/34450900674_15863ddea0_k_d.jpg + Python for iOS -------------- diff --git a/docs/community/recommended.rst b/docs/community/recommended.rst index ae2ae5eb..0f652d54 100644 --- a/docs/community/recommended.rst +++ b/docs/community/recommended.rst @@ -3,6 +3,8 @@ Recommended Packages and Extensions =================================== +.. image:: https://farm5.staticflickr.com/4218/35224319272_cfc0e621fb_k_d.jpg + Requests has a great variety of powerful and useful third-party extensions. This page provides an overview of some of the best of them. @@ -34,6 +36,14 @@ requested by users within the community. .. _Requests-Toolbelt: http://toolbelt.readthedocs.io/en/latest/index.html + +Requests-Threads +---------------- + +`Requests-Threads` is a Requests session that returns the amazing Twisted's awaitable Deferreds instead of Response objects. This allows the use of ``async``/``await`` keyword usage on Python 3, or Twisted's style of programming, if desired. + +.. _Requests-Threads: https://github.com/requests/requests-threads + Requests-OAuthlib ----------------- diff --git a/docs/community/release-process.rst b/docs/community/release-process.rst index c0943ddc..2e317ceb 100644 --- a/docs/community/release-process.rst +++ b/docs/community/release-process.rst @@ -1,6 +1,8 @@ Release Process and Rules ========================= +.. image:: https://farm5.staticflickr.com/4215/34450901614_b74ae720db_k_d.jpg + .. versionadded:: v2.6.2 Starting with the version to be released after ``v2.6.2``, the following rules diff --git a/docs/community/support.rst b/docs/community/support.rst index 4d5b0c35..02e8da75 100644 --- a/docs/community/support.rst +++ b/docs/community/support.rst @@ -3,6 +3,8 @@ Support ======= +.. image:: https://farm5.staticflickr.com/4198/34080352913_5c13ffb336_k_d.jpg + If you have questions or issues about Requests, there are several options: StackOverflow diff --git a/docs/community/updates.rst b/docs/community/updates.rst index e1f0d8db..f755a493 100644 --- a/docs/community/updates.rst +++ b/docs/community/updates.rst @@ -4,6 +4,8 @@ Community Updates ================= +.. image:: https://farm5.staticflickr.com/4244/34080354873_516c283ad0_k_d.jpg + If you'd like to stay up to date on the community and development of Requests, there are several options: diff --git a/docs/community/vulnerabilities.rst b/docs/community/vulnerabilities.rst index 5ff3f2cc..a6ab887e 100644 --- a/docs/community/vulnerabilities.rst +++ b/docs/community/vulnerabilities.rst @@ -1,6 +1,8 @@ Vulnerability Disclosure ======================== +.. image:: https://farm5.staticflickr.com/4211/34709353644_b041e9e1c2_k_d.jpg + If you think you have found a potential security vulnerability in requests, please email `sigmavirus24 `_ and `Lukasa `_ directly. **Do not file a public issue.** diff --git a/docs/conf.py b/docs/conf.py index b1c941e1..4bda98b0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -129,7 +129,8 @@ html_theme_options = { 'github_user': 'requests', 'github_repo': 'requests', 'github_banner': True, - 'show_related': False + 'show_related': False, + 'note_bg': '#FFF59C' } # Add any paths that contain custom themes here, relative to this directory. diff --git a/docs/dev/authors.rst b/docs/dev/authors.rst index 2e96919c..4cdd14cd 100644 --- a/docs/dev/authors.rst +++ b/docs/dev/authors.rst @@ -1,5 +1,6 @@ Authors ======= +.. image:: https://static1.squarespace.com/static/533ad9bde4b098d084a846b1/t/534f6e1ce4b09b70f38ee6c1/1432265542589/DSCF3147.jpg?format=2500w .. include:: ../../AUTHORS.rst diff --git a/docs/dev/contributing.rst b/docs/dev/contributing.rst index 265994b3..efc79bbf 100644 --- a/docs/dev/contributing.rst +++ b/docs/dev/contributing.rst @@ -3,6 +3,8 @@ Contributor's Guide =================== +.. image:: https://farm5.staticflickr.com/4237/35550408335_7671fde302_k_d.jpg + If you're reading this, you're probably interested in contributing to Requests. Thank you very much! Open source projects live-and-die based on the support they receive from others, and the fact that you're even considering @@ -203,4 +205,4 @@ while keeping an open ear and mind. If you believe there is a feature missing, feel free to raise a feature request, but please do be aware that the overwhelming likelihood is that your -feature request will not be accepted. \ No newline at end of file +feature request will not be accepted. diff --git a/docs/dev/philosophy.rst b/docs/dev/philosophy.rst index c0c0612f..563c551c 100644 --- a/docs/dev/philosophy.rst +++ b/docs/dev/philosophy.rst @@ -1,6 +1,8 @@ Development Philosophy ====================== +.. image:: https://farm5.staticflickr.com/4231/34484831073_636008a23d_k_d.jpg + Requests is an open but opinionated library, created by an open but opinionated developer. @@ -31,6 +33,10 @@ Standard Library? Requests has no *active* plans to be included in the standard library. This decision has been discussed at length with Guido as well as numerous core developers. +.. raw:: html + + + Essentially, the standard library is where a library goes to die. It is appropriate for a module to be included when active development is no longer necessary. Linux Distro Packages diff --git a/docs/dev/todo.rst b/docs/dev/todo.rst index 0f508caf..d960d305 100644 --- a/docs/dev/todo.rst +++ b/docs/dev/todo.rst @@ -1,6 +1,8 @@ How to Help =========== +.. image:: https://farm5.staticflickr.com/4290/34450900104_bc1d424213_k_d.jpg + 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 bug. @@ -50,7 +52,6 @@ Runtime Environments Requests currently supports the following versions of Python: - Python 2.7 -- Python 3.3 - Python 3.4 - Python 3.5 - Python 3.6 diff --git a/docs/index.rst b/docs/index.rst index 101e0450..9ee0fc79 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,10 +28,10 @@ Release v\ |version|. (:ref:`Installation `) **Requests** is the only *Non-GMO* HTTP library for Python, safe for human consumption. -*Warning: Recreational use of the Python standard library for HTTP may result in dangerous side-effects, -including: security vulnerabilities, verbose code, reinventing the wheel, -constantly reading documentation, depression, headaches, or even death.* - +.. note:: The use of **Python 3** is *highly* preferred over Python 2. Consider upgrading your applications and infrastructure if you find yourself *still* using Python 2 in production today. If you are using Python 3, congratulations — you are indeed a person of excellent taste. + —*Kenneth Reitz* + + ------------------- **Behold, the power of Requests**:: @@ -81,7 +81,7 @@ Institutions that prefer to be unnamed claim to use Requests internally. simple, Pythonic.* Requests is one of the most downloaded Python packages of all time, pulling in -over 11,000,000 downloads every month. All the cool kids are doing it! +over 13,000,000 downloads every month. All the cool kids are doing it! Beloved Features ---------------- @@ -104,7 +104,7 @@ Requests is ready for today's web. - Chunked Requests - ``.netrc`` Support -Requests officially supports Python 2.7 & 3.3–3.7, and runs great on PyPy. +Requests officially supports Python 2.7 & 3.4–3.7, and runs great on PyPy. The User Guide @@ -131,10 +131,10 @@ This part of the documentation, which is mostly prose, details the Requests ecosystem and community. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 - community/faq community/recommended + community/faq community/out-there community/support community/vulnerabilities diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index 2aac434c..613df205 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -3,6 +3,8 @@ Advanced Usage ============== +.. image:: https://farm5.staticflickr.com/4263/35163665790_d182d84f5e_k_d.jpg + This document covers some of Requests more advanced features. .. _session-objects: @@ -187,6 +189,25 @@ applied, replace the call to :meth:`Request.prepare() print(resp.status_code) +When you are using the prepared request flow, keep in mind that it does not take into account the environment. +This can cause problems if you are using environment variables to change the behaviour of requests. +For example: Self-signed SSL certificates specified in ``REQUESTS_CA_BUNDLE`` will not be taken into account. +As a result an ``SSL: CERTIFICATE_VERIFY_FAILED`` is thrown. +You can get around this behaviour by explicity merging the environment settings into your session:: + + from requests import Request, Session + + s = Session() + req = Request('GET', url) + + prepped = s.prepare_request(req) + + # Merge environment settings into session + settings = s.merge_environment_settings(prepped.url, None, None, None, None) + resp = s.send(prepped, **settings) + + print(resp.status_code) + .. _verification: SSL Cert Verification @@ -301,15 +322,11 @@ release the connection back to the pool unless you consume all the data or call :meth:`Response.close `. This can lead to inefficiency with connections. If you find yourself partially reading request bodies (or not reading them at all) while using ``stream=True``, you should -consider using ``contextlib.closing`` (`documented here`_), like this:: +make the request within a ``with`` statement to ensure it's always closed:: - from contextlib import closing - - with closing(requests.get('http://httpbin.org/get', stream=True)) as r: + with requests.get('http://httpbin.org/get', stream=True) as r: # Do things with the response here. -.. _`documented here`: http://docs.python.org/2/library/contextlib.html#contextlib.closing - .. _keep-alive: Keep-Alive @@ -597,6 +614,8 @@ as using a HTTP one:: 'https': 'socks5://user:pass@host:port' } +Using the scheme ``socks5`` causes the DNS resolution to happen on the client, rather than on the proxy server. This is in line with curl, which uses the scheme to decide whether to do the DNS resolution on the client or proxy. If you want to resolve the domains on the proxy server, use ``socks5h`` as the scheme. + .. _compliance: Compliance @@ -938,8 +957,9 @@ response at a time. However, these calls will still block. If you are concerned about the use of blocking IO, there are lots of projects out there that combine Requests with one of Python's asynchronicity frameworks. -Two excellent examples are `grequests`_ and `requests-futures`_. +Some excellent examples are `requests-threads`_, `grequests`_, and `requests-futures`_. +.. _`requests-threads`: https://github.com/requests/requests-threads .. _`grequests`: https://github.com/kennethreitz/grequests .. _`requests-futures`: https://github.com/ross/requests-futures diff --git a/docs/user/authentication.rst b/docs/user/authentication.rst index 2b7a8690..8ffab504 100644 --- a/docs/user/authentication.rst +++ b/docs/user/authentication.rst @@ -3,6 +3,8 @@ Authentication ============== +.. image:: https://farm5.staticflickr.com/4258/35550409215_3b08d49d22_k_d.jpg + This document discusses using various kinds of authentication with Requests. Many web services require authentication, and there are many different types. diff --git a/docs/user/install.rst b/docs/user/install.rst index 43ed789b..1dd9de8e 100644 --- a/docs/user/install.rst +++ b/docs/user/install.rst @@ -3,18 +3,21 @@ Installation of Requests ======================== +.. image:: https://farm5.staticflickr.com/4230/35550376215_da1bf77a8c_k_d.jpg + + This part of the documentation covers the installation of Requests. The first step to using any software package is getting it properly installed. -$ pip install requests ----------------------- +$ pipenv install requests +------------------------- To install Requests, simply run this simple command in your terminal of choice:: - $ pip install requests + $ pipenv install requests -If you don't have `pip `_ installed (tisk tisk!), +If you don't have `pipenv `_ installed (tisk tisk!), head over to the Pipenv website for installation instructions. Or, if you prefer to just use pip and don't have it installed, `this Python installation guide `_ can guide you through the process. diff --git a/docs/user/intro.rst b/docs/user/intro.rst index 816ad0a4..e8395d9d 100644 --- a/docs/user/intro.rst +++ b/docs/user/intro.rst @@ -3,6 +3,8 @@ Introduction ============ +.. image:: https://farm5.staticflickr.com/4317/35198386374_1939af3de6_k_d.jpg + Philosophy ---------- diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index 5b4640ea..109c3415 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -3,6 +3,8 @@ Quickstart ========== +.. image:: https://farm5.staticflickr.com/4259/35163667010_8bfcaef274_k_d.jpg + .. module:: requests.models Eager to get started? This page gives a good introduction in how to get started diff --git a/requests/__init__.py b/requests/__init__.py index c3286724..fc6bd1f2 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -40,44 +40,67 @@ is at . :license: Apache 2.0, see LICENSE for more details. """ -# Check urllib3 for compatibility. import urllib3 -urllib3_version = urllib3.__version__.split('.') -# Sometimes, urllib3 only reports its version as 16.1. -if len(urllib3_version) == 2: - urllib3_version.append('0') -major, minor, patch = urllib3_version -major, minor, patch = int(major), int(minor), int(patch) -# urllib3 >= 1.21.1, < 1.22 -try: +import chardet +import warnings +from .exceptions import RequestsDependencyWarning + + +def check_compatibility(urllib3_version, chardet_version): + urllib3_version = urllib3_version.split('.') + assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append('0') + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1, <= 1.22 assert major == 1 assert minor >= 21 assert minor <= 22 -except AssertionError: - raise RuntimeError('Requests dependency \'urllib3\' must be version >= 1.21.1, < 1.22!') - -# Check chardet for compatibility. -import chardet -major, minor, patch = chardet.__version__.split('.')[:3] -major, minor, patch = int(major), int(minor), int(patch) -# chardet >= 3.0.2, < 3.1.0 -try: + # Check chardet for compatibility. + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet >= 3.0.2, < 3.1.0 assert major == 3 assert minor < 1 assert patch >= 2 -except AssertionError: - raise RuntimeError('Requests dependency \'chardet\' must be version >= 3.0.2, < 3.1.0!') + + +def _check_cryptography(cryptography_version): + # cryptography < 1.3.4 + try: + cryptography_version = list(map(int, cryptography_version.split('.'))) + except ValueError: + return + + if cryptography_version < [1, 3, 4]: + warning = 'Old version of cryptography ({0}) may cause slowdown.'.format(cryptography_version) + warnings.warn(warning, RequestsDependencyWarning) + +# Check imported dependencies for compatibility. +try: + check_compatibility(urllib3.__version__, chardet.__version__) +except (AssertionError, ValueError): + warnings.warn("urllib3 ({0}) or chardet ({1}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet.__version__), + RequestsDependencyWarning) # Attempt to enable urllib3's SNI support, if possible try: from urllib3.contrib import pyopenssl pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + _check_cryptography(cryptography_version) except ImportError: pass -import warnings - # urllib3's DependencyWarnings should be silenced. from urllib3.exceptions import DependencyWarning warnings.simplefilter('ignore', DependencyWarning) diff --git a/requests/adapters.py b/requests/adapters.py index b2c1d4e3..5bf80eb5 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -513,6 +513,10 @@ class HTTPAdapter(BaseAdapter): if isinstance(e.reason, _ProxyError): raise ProxyError(e, request=request) + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + raise ConnectionError(e, request=request) except ClosedPoolError as e: @@ -523,6 +527,7 @@ class HTTPAdapter(BaseAdapter): except (_SSLError, _HTTPError) as e: if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 raise SSLError(e, request=request) elif isinstance(e, ReadTimeoutError): raise ReadTimeout(e, request=request) diff --git a/requests/api.py b/requests/api.py index 89890161..b02834b1 100644 --- a/requests/api.py +++ b/requests/api.py @@ -77,7 +77,7 @@ def get(url, params=None, **kwargs): def options(url, **kwargs): - r"""Sends a OPTIONS request. + r"""Sends an OPTIONS request. :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. diff --git a/requests/compat.py b/requests/compat.py index 0e2086f3..7fcd5492 100644 --- a/requests/compat.py +++ b/requests/compat.py @@ -25,7 +25,6 @@ is_py2 = (_ver[0] == 2) #: Python 3.x? is_py3 = (_ver[0] == 3) - # --------- # Specifics # --------- diff --git a/requests/exceptions.py b/requests/exceptions.py index 10142774..ebf4cc34 100644 --- a/requests/exceptions.py +++ b/requests/exceptions.py @@ -119,3 +119,8 @@ class RequestsWarning(Warning): class FileModeWarning(RequestsWarning, DeprecationWarning): """A file was opened in text mode, but Requests determined its binary length.""" pass + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" + pass diff --git a/requests/help.py b/requests/help.py index ac0691e3..5440ee61 100644 --- a/requests/help.py +++ b/requests/help.py @@ -6,6 +6,7 @@ import platform import sys import ssl +import idna import urllib3 import chardet @@ -23,7 +24,7 @@ else: def _implementation(): - """Return a dict with the Python implementation and verison. + """Return a dict with the Python implementation and version. Provide both the name and the version of the Python implementation currently running. For example, on CPython 2.7.5 it will return @@ -84,18 +85,26 @@ def info(): cryptography_info = { 'version': getattr(cryptography, '__version__', ''), } + idna_info = { + 'version': getattr(idna, '__version__', ''), + } + + # OPENSSL_VERSION_NUMBER doesn't exist in the Python 2.6 ssl module. + system_ssl = getattr(ssl, 'OPENSSL_VERSION_NUMBER', None) + system_ssl_info = { + 'version': '%x' % system_ssl if system_ssl is not None else '' + } return { 'platform': platform_info, 'implementation': implementation_info, - 'system_ssl': { - 'version': '%x' % ssl.OPENSSL_VERSION_NUMBER, - }, + 'system_ssl': system_ssl_info, 'using_pyopenssl': pyopenssl is not None, 'pyOpenSSL': pyopenssl_info, 'urllib3': urllib3_info, 'chardet': chardet_info, 'cryptography': cryptography_info, + 'idna': idna_info, 'requests': { 'version': requests_version, }, diff --git a/requests/models.py b/requests/models.py index cb20d037..8eab3f64 100644 --- a/requests/models.py +++ b/requests/models.py @@ -598,8 +598,6 @@ class Response(object): ] def __init__(self): - super(Response, self).__init__() - self._content = False self._content_consumed = False self._next = None @@ -647,6 +645,12 @@ class Response(object): #: is a response. self.request = None + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + def __getstate__(self): # Consume everything; accessing the content attribute makes # sure the content has been fully read. diff --git a/requests/sessions.py b/requests/sessions.py old mode 100755 new mode 100644 index d485d1f0..824b371e --- a/requests/sessions.py +++ b/requests/sessions.py @@ -41,7 +41,7 @@ from .models import REDIRECT_STATI # Preferred clock, based on which one is more accurate on a given system. if platform.system() == 'Windows': - try: # Python 3.3+ + try: # Python 3.4+ preferred_clock = time.perf_counter except AttributeError: # Earlier than Python 3. preferred_clock = time.clock @@ -99,12 +99,19 @@ class SessionRedirectMixin(object): def get_redirect_target(self, response): """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. if response.is_redirect: if not is_valid_location(response): raise InvalidHeader('Response contains multiple Location headers. ' 'Unable to perform redirect.') location = response.headers['location'] + # Currently the underlying http module on py3 decode headers # in latin1, but empirical evidence suggests that latin1 is very # rarely used with non-ASCII characters in HTTP headers. @@ -727,7 +734,7 @@ class Session(SessionRedirectMixin): def mount(self, prefix, adapter): """Registers a connection adapter to a prefix. - Adapters are sorted in descending order by key length. + Adapters are sorted in descending order by prefix length. """ self.adapters[prefix] = adapter keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] diff --git a/requests/utils.py b/requests/utils.py index e619bf09..37f3276a 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -626,18 +626,18 @@ def set_environ(env_name, value): the environment variable 'env_name'. If 'value' is None, do nothing""" - if value is not None: + value_changed = value is not None + if value_changed: old_value = os.environ.get(env_name) os.environ[env_name] = value try: yield finally: - if value is None: - return - if old_value is None: - del os.environ[env_name] - else: - os.environ[env_name] = old_value + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value def should_bypass_proxies(url, no_proxy): @@ -743,7 +743,7 @@ def default_headers(): def parse_header_links(value): - """Return a dict of parsed link headers proxies. + """Return a list of parsed link headers proxies. i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" @@ -754,6 +754,10 @@ def parse_header_links(value): replace_chars = ' \'"' + value = value.strip(replace_chars) + if not value: + return links + for val in re.split(', *<', value): try: url, params = val.split(';', 1) @@ -881,8 +885,8 @@ def check_header_validity(header): if not pat.match(value): raise InvalidHeader("Invalid return character or leading space in header: %s" % name) except TypeError: - raise InvalidHeader("Header value %s must be of type str or bytes, " - "not %s" % (value, type(value))) + raise InvalidHeader("Value for header {%s: %s} must be of type str or " + "bytes, not %s" % (name, value, type(value))) def urldefragauth(url): diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8d79283f..00000000 --- a/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ --e .[socks] -pytest>=2.8.0 -codecov -pytest-httpbin==0.0.7 -pytest-mock -pytest-cov -pytest-xdist -alabaster -readme_renderer -Sphinx<=1.5.5 -PySocks -setuptools>=18.5 -docutils -flake8 -tox -detox diff --git a/setup.py b/setup.py index b0e7ddb5..2782b1c7 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# Learn more: https://github.com/kennethreitz/setup.py import os import re @@ -43,8 +44,8 @@ packages = ['requests'] requires = [ 'chardet>=3.0.2,<3.1.0', - 'idna>=2.5,<2.6', - 'urllib3>=1.21.1,<1.22', + 'idna>=2.5,<2.7', + 'urllib3>=1.21.1,<1.23', 'certifi>=2017.4.17' ] @@ -83,7 +84,6 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/tests/test_help.py b/tests/test_help.py new file mode 100644 index 00000000..c11d43f3 --- /dev/null +++ b/tests/test_help.py @@ -0,0 +1,40 @@ +# -*- encoding: utf-8 + +import sys + +import pytest + +from requests.help import info + + +@pytest.mark.skipif(sys.version_info[:2] != (2,6), reason="Only run on Python 2.6") +def test_system_ssl_py26(): + """OPENSSL_VERSION_NUMBER isn't provided in Python 2.6, verify we don't + blow up in this case. + """ + assert info()['system_ssl'] == {'version': ''} + + +@pytest.mark.skipif(sys.version_info < (2,7), reason="Only run on Python 2.7+") +def test_system_ssl(): + """Verify we're actually setting system_ssl when it should be available.""" + assert info()['system_ssl']['version'] != '' + + +class VersionedPackage(object): + def __init__(self, version): + self.__version__ = version + + +def test_idna_without_version_attribute(mocker): + """Older versions of IDNA don't provide a __version__ attribute, verify + that if we have such a package, we don't blow up. + """ + mocker.patch('requests.help.idna', new=None) + assert info()['idna'] == {'version': ''} + + +def test_idna_with_version_attribute(mocker): + """Verify we're actually setting idna version when it should be available.""" + mocker.patch('requests.help.idna', new=VersionedPackage('2.6')) + assert info()['idna'] == {'version': '2.6'} diff --git a/tests/test_lowlevel.py b/tests/test_lowlevel.py old mode 100755 new mode 100644 diff --git a/tests/test_requests.py b/tests/test_requests.py old mode 100755 new mode 100644 index 272d3fd4..8c06df78 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for Requests.""" @@ -25,7 +24,8 @@ from requests.cookies import ( from requests.exceptions import ( ConnectionError, ConnectTimeout, InvalidScheme, InvalidURL, MissingScheme, ReadTimeout, Timeout, RetryError, TooManyRedirects, - ProxyError, InvalidHeader, UnrewindableBodyError, InvalidBodyError) + ProxyError, InvalidHeader, UnrewindableBodyError, InvalidBodyError, + SSLError) from requests.models import PreparedRequest from requests.structures import CaseInsensitiveDict from requests.sessions import SessionRedirectMixin @@ -749,7 +749,7 @@ class TestRequests: post1 = requests.post(url, data={'some': 'data'}) assert post1.status_code == 200 - with open('requirements.txt') as f: + with open('Pipfile') as f: post2 = requests.post(url, files={'some': f}) assert post2.status_code == 200 @@ -809,7 +809,7 @@ class TestRequests: post1 = requests.post(url, data={'some': 'data'}) assert post1.status_code == 200 - with open('requirements.txt') as f: + with open('Pipfile') as f: post2 = requests.post(url, data={'some': 'data'}, files={'some': f}) assert post2.status_code == 200 @@ -846,7 +846,7 @@ class TestRequests: def test_conflicting_post_params(self, httpbin): url = httpbin('post') - with open('requirements.txt') as f: + with open('Pipfile') as f: pytest.raises(ValueError, "requests.post(url, data='[{\"some\": \"data\"}]', files={'some': f})") pytest.raises(ValueError, "requests.post(url, data=u('[{\"some\": \"data\"}]'), files={'some': f})") @@ -931,6 +931,15 @@ class TestRequests: item.category.__name__ for item in warning_records) assert warnings_category == warnings_expected + def test_certificate_failure(self, httpbin_secure): + """ + When underlying SSL problems occur, an SSLError is raised. + """ + with pytest.raises(SSLError): + # Our local httpbin does not have a trusted CA, so this call will + # fail if we use our default trust bundle. + requests.get(httpbin_secure('status', '200')) + def test_urlencoded_get_query_multivalued_param(self, httpbin): r = requests.get(httpbin('get'), params={'test': ['foo', 'baz']}) @@ -1629,14 +1638,17 @@ class TestRequests: headers_list = {'baz': ['foo', 'bar']} # Test for int - with pytest.raises(InvalidHeader): + with pytest.raises(InvalidHeader) as excinfo: r = requests.get(httpbin('get'), headers=headers_int) + assert 'foo' in str(excinfo.value) # Test for dict - with pytest.raises(InvalidHeader): + with pytest.raises(InvalidHeader) as excinfo: r = requests.get(httpbin('get'), headers=headers_dict) + assert 'bar' in str(excinfo.value) # Test for list - with pytest.raises(InvalidHeader): + with pytest.raises(InvalidHeader) as excinfo: r = requests.get(httpbin('get'), headers=headers_list) + assert 'baz' in str(excinfo.value) def test_header_no_return_chars(self, httpbin): """Ensure that a header containing return character sequences raise an @@ -1906,6 +1918,12 @@ class TestRequests: next(it) assert len(list(it)) == 3 + def test_response_context_manager(self, httpbin): + with requests.get(httpbin('stream/4'), stream=True) as response: + assert isinstance(response, requests.Response) + + assert response.raw.closed + def test_unconsumed_session_response_closes_connection(self, httpbin): s = requests.session() @@ -2760,7 +2778,7 @@ class TestPreparingURLs(object): ) def test_parameters_for_nonstandard_schemes(self, input, params, expected): """ - Setting paramters for nonstandard schemes is allowed if those schemes + Setting parameters for nonstandard schemes is allowed if those schemes begin with "http", and is forbidden otherwise. """ r = requests.Request('GET', url=input, params=params) diff --git a/tests/test_utils.py b/tests/test_utils.py index 04baad49..0cd93d7d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os +import copy from io import BytesIO import pytest @@ -17,7 +18,7 @@ from requests.utils import ( requote_uri, select_proxy, should_bypass_proxies, super_len, to_key_val_list, to_native_string, unquote_header_value, unquote_unreserved, - urldefragauth, add_dict_to_cookiejar) + urldefragauth, add_dict_to_cookiejar, set_environ) from requests._internal_utils import unicode_is_ascii from .compat import StringIO, cStringIO @@ -497,6 +498,10 @@ def test_iter_slices(value, length): {'url': 'http://.../back.jpeg'} ] ), + ( + '', + [] + ), )) def test_parse_header_links(value, expected): assert parse_header_links(value) == expected @@ -651,4 +656,29 @@ def test_should_bypass_proxies_win_registry(url, expected, override, monkeypatch.setenv('NO_PROXY', '') monkeypatch.setattr(winreg, 'OpenKey', OpenKey) monkeypatch.setattr(winreg, 'QueryValueEx', QueryValueEx) - assert should_bypass_proxies(url, no_proxy=None) == expected + + +@pytest.mark.parametrize( + 'env_name, value', ( + ('no_proxy', '192.168.0.0/24,127.0.0.1,localhost.localdomain'), + ('no_proxy', None), + ('a_new_key', '192.168.0.0/24,127.0.0.1,localhost.localdomain'), + ('a_new_key', None), + )) +def test_set_environ(env_name, value): + """Tests set_environ will set environ values and will restore the environ.""" + environ_copy = copy.deepcopy(os.environ) + with set_environ(env_name, value): + assert os.environ.get(env_name) == value + + assert os.environ == environ_copy + + +def test_set_environ_raises_exception(): + """Tests set_environ will raise exceptions in context when the + value parameter is None.""" + with pytest.raises(Exception) as exception: + with set_environ('test1', None): + raise Exception('Expected exception') + + assert 'Expected exception' in str(exception.value)