Compare commits

...

359 Commits

Author SHA1 Message Date
Nate Prewitt
35ba878a72 Move class types to init where possible
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.14) (push) Has been cancelled
Tests / build (macOS-latest, 3.15-dev) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.14) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.15-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.14) (push) Has been cancelled
Tests / build (windows-latest, 3.15-dev) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
Type Check / typecheck (3.10) (push) Has been cancelled
Type Check / typecheck (3.14) (push) Has been cancelled
2026-05-07 18:14:26 -06:00
dependabot[bot]
04d750509b Bump https://github.com/astral-sh/ruff-pre-commit (#7411)
Bumps the pre-commit group with 1 update: [https://github.com/astral-sh/ruff-pre-commit](https://github.com/astral-sh/ruff-pre-commit).


Updates `https://github.com/astral-sh/ruff-pre-commit` from v0.15.11 to 0.15.12
- [Release notes](https://github.com/astral-sh/ruff-pre-commit/releases)
- [Commits](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.11...v0.15.12)

---
updated-dependencies:
- dependency-name: https://github.com/astral-sh/ruff-pre-commit
  dependency-version: 0.15.12
  dependency-type: direct:production
  dependency-group: pre-commit
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 14:42:06 -07:00
Nate Prewitt
f7822f7c41 v2.34.0.dev1
Some checks failed
Lint code / lint (push) Has been cancelled
Publish to PyPI / Build dists (push) Has been cancelled
Publish to PyPI / Publish (push) Has been cancelled
Publish to PyPI / Publish to Test PyPI (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.14) (push) Has been cancelled
Tests / build (macOS-latest, 3.15-dev) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.14) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.15-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.14) (push) Has been cancelled
Tests / build (windows-latest, 3.15-dev) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
Type Check / typecheck (3.10) (push) Has been cancelled
Type Check / typecheck (3.14) (push) Has been cancelled
2026-05-03 12:51:27 -07:00
Nate Prewitt
561e4b6889 Add inline types to Requests (#7272) 2026-05-03 12:38:48 -07:00
Nate Prewitt
8f6cda9969 Add tests for hasattr/getattr usage on LookupDict (#7401) 2026-04-30 10:05:37 -07:00
Dimitri Papadopoulos Orfanos
27e0981962 Fix remaining typos (#7395) 2026-04-30 07:35:13 -07:00
Dimitri Papadopoulos Orfanos
6ec76b4a36 Ruff rule UP038 has been removed (#7393) 2026-04-27 08:08:18 -07:00
dependabot[bot]
f43f750ee1 Bump https://github.com/astral-sh/ruff-pre-commit (#7390) 2026-04-24 08:19:25 -06:00
Nate Prewitt
9450dd51fb Create groups for GHA and pre-commit dependabot PRs (#7387) 2026-04-24 05:52:01 -05:00
Nate Prewitt
93bf5331a7 Update index.rst to match README info (#7386) 2026-04-23 13:53:48 -07:00
dependabot[bot]
79f4df84cf Bump pypa/gh-action-pypi-publish from 1.13.0 to 1.14.0 (#7378)
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](ed0c53931b...cef221092e)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 12:32:32 -07:00
Dejan SANADER
b294b08fe3 Consider urllib3 version has 3 numbers at most (#7375) (#7376) 2026-04-20 09:24:32 -07:00
Christian Clauss
5f3ff9b9e4 Fix typos discovered by codespell (#7371) 2026-04-19 11:34:20 -07:00
Nate Prewitt
514c1623fe Update README and remove extraneous images (#7366) 2026-04-15 11:30:57 -07:00
Chris Riches
d2f6bdecc8 Clarify decode_unicode behaviour in iter_{content,lines} docstrings (#7365) 2026-04-15 08:06:46 -07:00
Nate Prewitt
a044b020de Move DigestAuth hash algorithms to use usedforsecurity=False (#7310) 2026-04-15 07:56:38 -07:00
Nate Prewitt
16df2a0917 Move pytest pin to support 9.x series (#7364) 2026-04-14 20:04:46 -07:00
Nate Prewitt
fe2063be0c Don't hide navigation on mobile webpage (#7360) 2026-04-14 10:58:28 -07:00
Nate Prewitt
4b0b1a3e9f Update pre-commit versions (#7348)
* Update pre-commit versions

* Add dependabot entry for pre-commit

* Update pre-commit and ruff hooks
2026-04-11 19:05:37 -05:00
Nate Prewitt
185f587a78 Cleanup docs and add i18n wrappers (#7354) 2026-04-11 13:39:24 -06:00
Nate Prewitt
ef439eb779 Prevent Response self-reference in redirect history (#7328) 2026-04-05 08:31:02 -07:00
Nate Prewitt
cbce031327 Add Zizmor workflow and make initial updates 2026-03-31 21:28:16 -05:00
dependabot[bot]
6360477c52 Bump github/codeql-action from 4.34.1 to 4.35.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.34.1 to 4.35.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3869755554...c10b8064de)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 12:33:02 -06:00
Nate Prewitt
111d2b7779 v2.33.1
Some checks failed
Lint code / lint (push) Has been cancelled
Publish to PyPI / Build dists (push) Has been cancelled
Publish to PyPI / Publish (push) Has been cancelled
Publish to PyPI / Publish to Test PyPI (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.14) (push) Has been cancelled
Tests / build (macOS-latest, 3.15-dev) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.14) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.15-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.14) (push) Has been cancelled
Tests / build (windows-latest, 3.15-dev) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
2026-03-30 09:07:00 -07:00
Nate Prewitt
f0198e6dfc Fix malformed value parsing for Content-Type (#7309) 2026-03-30 08:27:35 -07:00
Nate Prewitt
bc7dd0fc4d Fix cosmetic header validity parsing regex (#7308) 2026-03-30 08:24:49 -07:00
Nate Prewitt
4443b1a847 Fix unintended test extra (#7306) 2026-03-30 08:23:00 -07:00
Nate Prewitt
389eea58df Cleanup extracted file after extract_zipped_path test (#7305) 2026-03-30 08:21:38 -07:00
Frank Dana
7407309c8a Packaging: DRY out extras definition (#7277) 2026-03-29 21:07:31 -07:00
Nate Prewitt
bc04dfd6da v2.33.0
Some checks failed
Lint code / lint (push) Has been cancelled
Publish to PyPI / Build dists (push) Has been cancelled
Publish to PyPI / Publish (push) Has been cancelled
Publish to PyPI / Publish to Test PyPI (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.14) (push) Has been cancelled
Tests / build (macOS-latest, 3.15-dev) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.14) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.15-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.14) (push) Has been cancelled
Tests / build (windows-latest, 3.15-dev) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
2026-03-25 08:06:17 -07:00
Nate Prewitt
66d21cb07b Merge commit from fork 2026-03-25 07:57:56 -07:00
Nate Prewitt
8b9bc8fc0f Move badges to top of README (#7293) 2026-03-25 05:18:38 +00:00
Nate Prewitt
e331a288f3 Remove unused extraction call (#7292) 2026-03-24 22:15:21 -07:00
rohan436
753fd08c5e docs: fix FAQ grammar in httplib2 example
Signed-off-by: rohan436 <rohan.santhoshkumar@googlemail.com>
2026-03-24 19:36:48 -06:00
Magsen (CD)
774a0b837a docs(socks): same block as other sections
strange formatting:
https://docs.python-requests.org/en/latest/user/advanced/#socks
2026-03-24 19:26:47 -06:00
dependabot[bot]
9c72a41bec Bump github/codeql-action from 4.33.0 to 4.34.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.33.0 to 4.34.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](b1bff81932...3869755554)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.34.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 11:43:09 -06:00
dependabot[bot]
ebf7190679 Bump github/codeql-action from 4.32.0 to 4.33.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.32.0 to 4.33.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](b20883b0cd...b1bff81932)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 12:44:23 -06:00
Dr Alex Mitre
0e4ae38f0c docs: exclude Response.is_permanent_redirect from API docs (#7244)
Hide the internal Response.is_permanent_redirect attribute from public API documentation.
2026-03-05 13:14:29 -07:00
Ethan Price
d568f47278 docs: clarify Quickstart POST example (#6960)
(fixes #6949)

---------

Co-authored-by: Nate Prewitt <nate.prewitt@gmail.com>
2026-03-03 12:59:47 -07:00
Dan Blanchard
b2a1d33f57 Increase chardet upper limit to 8
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:46:41 -07:00
dependabot[bot]
392b01f26e Bump actions/download-artifact from 7.0.0 to 8.0.0
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...70fc10c6e5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 13:45:19 -07:00
dependabot[bot]
27f8b40d5c Bump actions/upload-artifact from 6.0.0 to 7.0.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](b7c566a772...bbbca2ddaa)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 13:33:25 -07:00
Amin Vakil
4bd79e3973 Increase chardet upper limit to 7 2026-02-22 16:02:37 -06:00
Nate Prewitt
da9113c046 Add CODEOWNERS file to repo (#7208) 2026-02-13 18:06:57 -06:00
Nate Prewitt
a2ce3d8ace Remove harden runner from publish workflow (#7207) 2026-02-13 16:48:20 -06:00
Nate Prewitt
5e7cb71cd7 Bump version to dev for test publish 2026-02-13 13:23:44 -07:00
Nate Prewitt
00600b38f4 Update publish workflow and add test pypi option 2026-02-13 13:23:44 -07:00
m0d3v
5f338446f9 Update docstring for Session.verify to include string support (#6859) (#6865)
---------

Co-authored-by: Nate Prewitt <nate.prewitt@gmail.com>
2026-02-12 19:52:04 -07:00
Nate Prewitt
47914226c2 Fix empty netrc entry usage (#7205) 2026-02-12 19:07:05 -07:00
Nate Prewitt
1b40fdd004 Merge pull request #7200 from psf/dependabot/github_actions/step-security/harden-runner-2.14.2
Bump step-security/harden-runner from 2.13.0 to 2.14.2
2026-02-10 17:29:35 -07:00
dependabot[bot]
514b3f2345 Bump step-security/harden-runner from 2.13.0 to 2.14.2
Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.13.0 to 2.14.2.
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](ec9f2d5744...5ef0c079ce)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.14.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 00:23:00 +00:00
Nate Prewitt
dedee0f456 Merge pull request #7199 from psf/dependabot/github_actions/dessant/lock-threads-6.0.0
Bump dessant/lock-threads from 5.0.0 to 6.0.0
2026-02-10 17:22:16 -07:00
dependabot[bot]
7112775514 Bump dessant/lock-threads from 5.0.0 to 6.0.0
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md)
- [Commits](d42e5f4980...7266a7ce5c)

---
updated-dependencies:
- dependency-name: dessant/lock-threads
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 23:59:55 +00:00
Nate Prewitt
733b2018a3 Update floors for certifi and urllib3 (#7189) 2026-02-10 16:36:31 -07:00
Nate Prewitt
0c2bbe7398 Drop support for Python 3.9 (#7196) 2026-02-06 08:02:46 -07:00
Nate Prewitt
32e26af3af Start testing Python 3.15 Alpha (#7195)
* Add optional 3.15-dev runners to test matrix

* Set PYO3_USE_ABI3_FORWARD_COMPATIBILITY for 3.15 builds
2026-02-04 19:01:16 -06:00
Nate Prewitt
421b8733cf Merge pull request #7101 from psf/dependabot/github_actions/github/codeql-action-4.31.6
Bump github/codeql-action from 4.30.8 to 4.31.6
2026-01-30 20:01:53 -07:00
dependabot[bot]
968863678b Bump github/codeql-action from 4.30.8 to 4.31.6
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.8 to 4.31.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f443b600d9...fe4161a26a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-30 20:00:02 -07:00
Nate Prewitt
0b950d8e97 Merge pull request #7100 from psf/dependabot/github_actions/actions/setup-python-6.1.0
Bump actions/setup-python from 6.0.0 to 6.1.0
2026-01-30 19:59:20 -07:00
dependabot[bot]
42eaeb4da8 Bump actions/setup-python from 6.0.0 to 6.1.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](e797f83bcb...83679a892e)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-30 19:53:54 -07:00
Nate Prewitt
49f8968867 Merge pull request #7092 from psf/dependabot/github_actions/actions/checkout-6.0.0
Bump actions/checkout from 5.0.0 to 6.0.0
2026-01-30 19:47:47 -07:00
dependabot[bot]
8e398336f8 Bump actions/checkout from 5.0.0 to 6.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](08c6903cd8...1af3b93b68)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-30 19:46:03 -07:00
Nate Prewitt
5f33bddabd Merge pull request #7073 from psf/dependabot/github_actions/actions/download-artifact-6.0.0
Bump actions/download-artifact from 5.0.0 to 6.0.0
2026-01-30 19:45:13 -07:00
dependabot[bot]
ca78ca9611 Bump actions/download-artifact from 5.0.0 to 6.0.0
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](634f93cb29...018cc2cf5b)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-30 19:38:25 -07:00
Nate Prewitt
8e5ac8e0eb Merge pull request #7071 from psf/dependabot/github_actions/actions/upload-artifact-5.0.0
Bump actions/upload-artifact from 4.6.2 to 5.0.0
2026-01-30 19:36:42 -07:00
dependabot[bot]
6c88b6b09f Bump actions/upload-artifact from 4.6.2 to 5.0.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 5.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](ea165f8d65...330a01c490)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-31 02:31:13 +00:00
Nate Prewitt
cb2c800518 Migrate build system to PEP 517 (#7012) 2026-01-30 19:25:55 -07:00
Nate Prewitt
a389aaad45 Update maintainers in the AUTHORS.rst (#7191)
---------

Co-authored-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
2026-01-30 19:12:40 -07:00
Nate Prewitt
f8bec2f7ca Fix CI and build failures (#7190)
* Migrate linting from various tooling to Ruff

* Run ruff over codebase

* Drop support for pypy-3.10
2026-01-30 15:10:27 -06:00
Nate Prewitt
7029833289 Merge pull request #7042 from psf/dependabot/github_actions/github/codeql-action-4.30.8 2025-10-15 20:45:42 +09:00
dependabot[bot]
6e4134b204 Bump github/codeql-action from 3.30.0 to 4.30.8
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.0 to 4.30.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](2d92b76c45...f443b600d9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.30.8
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 16:16:05 +00:00
Nate Prewitt
420d16bc7e Merge pull request #7026 from psf/dependabot/github_actions/actions/setup-python-6.0.0 2025-09-09 18:00:19 +09:00
dependabot[bot]
3c8decb92f Bump actions/setup-python from 5.6.0 to 6.0.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.6.0 to 6.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](a26af69be9...e797f83bcb)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 00:09:47 +00:00
Nate Prewitt
5a270e2a95 Merge pull request #7025 from psf/dependabot/github_actions/pypa/gh-action-pypi-publish-1.13.0 2025-09-09 09:08:49 +09:00
dependabot[bot]
7d8df1df57 Bump pypa/gh-action-pypi-publish from 1.12.4 to 1.13.0
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.4 to 1.13.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](76f52bc884...ed0c53931b)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 16:18:37 +00:00
dependabot[bot]
1c49660b84 Bump github/codeql-action from 3.29.0 to 3.30.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.0 to 3.30.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](ce28f5bb42...2d92b76c45)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 03:15:01 -05:00
Nate Prewitt
b25c87d7cb v2.32.5
Some checks failed
Lint code / lint (push) Has been cancelled
Publish to PyPI / Build dists (push) Has been cancelled
Publish to PyPI / provenance (push) Has been cancelled
Publish to PyPI / Publish (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.14-dev) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.14-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.14-dev) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
2025-08-18 13:23:20 -07:00
Nate Prewitt
131e506079 Merge pull request #7010 from psf/dependabot/github_actions/actions/checkout-5.0.0
Bump actions/checkout from 4.2.0 to 5.0.0
2025-08-11 15:32:16 -07:00
dependabot[bot]
b336cb2bc6 Bump actions/checkout from 4.2.0 to 5.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.0...08c6903cd8c0fde910a37f88322edcfb5dd907a8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 22:15:22 +00:00
Grant Birkinbine
46e939b552 Update publish workflow to use artifact-id instead of name
Added artifact-id output to publish workflow and updated download-artifact action version to `v5.0.0`. Also hardens the workflow a bit by adding `persist-credentials: false` to the checkout step
2025-08-06 07:36:19 -05:00
Nate Prewitt
4b9c546aa3 Merge pull request #6999 from psf/dependabot/github_actions/step-security/harden-runner-2.13.0
Bump step-security/harden-runner from 2.12.0 to 2.13.0
2025-07-28 12:09:42 -07:00
dependabot[bot]
7618dbef01 Bump step-security/harden-runner from 2.12.0 to 2.13.0
Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.12.0 to 2.13.0.
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](0634a2670c...ec9f2d5744)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 19:01:25 +00:00
Nate Prewitt
2edca11103 Add support for Python 3.14 and drop support for Python 3.8 (#6993)
* Add testing for Python 3.14 preview build

* Add trove classifier for Python 3.14

* Remove support for Python 3.8
2025-07-25 14:42:15 -06:00
Abhishek Jha
fec96cd597 Update Makefile rules (#6996) 2025-07-18 10:24:31 -06:00
Quaylyn Rimer
d58d8aa2f4 docs: clarify timeout parameter uses seconds in Session.request (#6994) 2025-07-18 02:09:19 +00:00
dependabot[bot]
91a3eabd3d Bump github/codeql-action from 3.28.5 to 3.29.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.5 to 3.29.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f6091c0113...ce28f5bb42)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 14:10:36 -05:00
Nate Prewitt
90fee0876a Revert caching a default SSLContext (#6767) 2025-06-13 10:42:08 -06:00
AHMAD FAIZ
8ff173b186 Clarify error description in cloning instructions
This clarifies the error description in the 'Cloning the repository' section of the README. The current wording mentions 'an error about a bad commit', which is somewhat vague. I've updated it to 'an error about a bad commit timestamp', which more accurately describes the nature of the error referenced in issue #2690.
This small change improves the clarity of the documentation, helping users better understand the potential issue they might encounter when cloning the repository and why they need to use the specified Git flag.
2025-06-10 07:40:33 -05:00
Jonas Fonseca
3f07f990ac Fix typo in documentation for verify 2025-06-09 21:54:25 -05:00
Ian Stapleton Cordasco
e57b5bf05c Add Trusted Publishing Release Workflow
Rather than rely on manual releases from a developer laptop, let's use
tag pushes to trigger a workflow to publish artifacts to PyPI. This will
leverage trusted publishing and upload attestations as well.
2025-06-09 21:32:45 -05:00
Ian Stapleton Cordasco
021dc729f0 Polish up release tooling for last manual release
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.13) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.13) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.13) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
Tests / urllib3 1.x (push) Has been cancelled
2025-06-09 11:39:48 -05:00
Ian Stapleton Cordasco
821770e822 Bump version and add release notes for v2.32.4 2025-06-09 11:39:48 -05:00
Piotr Szlazak
59f8aa2adf Add netrc file search information to authentication documentation (#6876) 2025-06-08 12:47:55 -05:00
Arthur Woimbée
5b4b64c346 Add more tests to prevent regression of CVE 2024 47081
Remove workaround not needed since py38 for os.path.expanduser.
2025-06-05 09:55:33 -05:00
danigm
7bc45877a8 Add new test to check netrc auth leak (#6962)
This patch adds a new test that reproduces the security issue reported
here:
https://seclists.org/oss-sec/2025/q2/204

Doing a request to a malicious url with a prefix like "domain.com:@"
will use the "domain.com" netrc credentials in the request to other
domain.
2025-06-05 06:21:46 -05:00
Nate Prewitt
96ba401c12 Only use hostname to do netrc lookup instead of netloc 2025-06-04 10:39:25 -05:00
Nate Prewitt
7341690e84 Merge pull request #6951 from tswast/patch-1 2025-06-01 11:07:17 -07:00
Tim Sweña
6716d7c9f2 remove links 2025-06-01 09:03:51 -05:00
Ian Stapleton Cordasco
a7e1c745dc Update docs/conf.py
Co-authored-by: Nate Prewitt <nate.prewitt@gmail.com>
2025-05-31 11:59:51 -05:00
Tim Sweña (Swast)
c799b8167a docs: fix dead links to kenreitz.org 2025-05-21 11:08:05 -05:00
Robin
c65c780849 Add two more tests exercising the adapter (#6936)
Closes #6935
2025-05-03 11:39:14 -05:00
Nate Prewitt
579cd9f233 Drop pypy 3.9 and add pypy 3.11 support 2025-05-01 21:54:57 -05:00
Nate Prewitt
c54d0347e2 Merge pull request #6939 from psf/dependabot/github_actions/actions/setup-python-5.6.0 2025-04-28 10:36:20 -07:00
dependabot[bot]
991f05dcd9 Bump actions/setup-python from 5.5.0 to 5.6.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.5.0 to 5.6.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](8d9ed9ac5c...a26af69be9)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 17:28:05 +00:00
Robin
4ce9520a1c Update lint workflow to ubuntu-24.04
Ubuntu 20.04 was the old default and is no longer supported by GitHub Actions
2025-04-23 07:44:05 -05:00
Ian Stapleton Cordasco
42a1dd640c Merge pull request #6924 from RichieB2B/rfc5280
Add key usage extension to test ca.crt
2025-04-15 21:46:50 -05:00
Nate Prewitt
c4c8e20289 Merge pull request #6925 from psf/dependabot/github_actions/actions/setup-python-5.5.0
Bump actions/setup-python from 5.4.0 to 5.5.0
2025-03-31 09:43:07 -07:00
dependabot[bot]
a5cb4284e1 Bump actions/setup-python from 5.4.0 to 5.5.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](42375524e2...8d9ed9ac5c)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 16:31:10 +00:00
Richard van den Berg
2019450b43 Add key usage extension to test ca.crt 2025-03-29 14:53:45 +01:00
Nate Prewitt
1764cc938e Merge pull request #6899 from duzhuoshanwai/main
Docs: Add quotes to prevent Zsh wildcard interpretation.
2025-02-20 10:43:13 -08:00
duzhuoshanwai
e946665c59 Update advanced.rst
Add quotes to prevent Zsh wildcard interpretation.
2025-02-19 10:05:28 +08:00
Ian Stapleton Cordasco
8ba805fa93 Merge pull request #6897 from cjwatson/fix-test-certificates
Add CA constraint to test CA
2025-02-17 06:40:19 -06:00
Colin Watson
9ebebdef98 Regenerate test certificates
Created using the following command with OpenSSL 3.4.0:

    for cert in expired mtls valid/server; do
        make -C tests/certs/$cert clean all
    done
2025-02-17 00:41:30 +00:00
Colin Watson
5074096613 Add CA constraint to test CA
Otherwise recent versions of OpenSSL reject it as an invalid CA
certificate (at least once the test certificates are regenerated).

Fixes: #6896
2025-02-17 00:39:07 +00:00
Nate Prewitt
1b7c37ef4e Merge pull request #6529 from anupam-arista/patch-1 2025-02-13 08:49:49 -08:00
anupam-arista
5d31ddbf7b Update src/requests/models.py
Co-authored-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
2025-02-12 17:58:07 +05:30
anupam-arista
a2fd25ff34 Update models.py 2025-02-11 21:28:59 +05:30
anupam-arista
d96f9982a2 Update src/requests/models.py
Co-authored-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
2025-02-11 21:25:29 +05:30
anupam-arista
b25f2b5ec2 Merge branch 'psf:main' into patch-1 2025-02-11 07:49:26 +05:30
Nate Prewitt
f761e74a4d Merge pull request #6880 from jakobheine/td/remove-urlrequired-from-docs
docs(exceptions): Remove unused exception URLRequired from documentation
2025-02-10 13:13:26 -08:00
Nate Prewitt
f2a6ae5e32 Merge pull request #6883 from psf/dependabot/github_actions/actions/setup-python-5.4.0
Bump actions/setup-python from 5.3.0 to 5.4.0
2025-02-03 08:58:57 -08:00
dependabot[bot]
11f63a330b Bump actions/setup-python from 5.3.0 to 5.4.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](0b93645e9f...42375524e2)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 16:27:40 +00:00
jakobheine
e361622fae docs(exceptions): Remove unused exception URLRequired from documentation
The documentation previously listed `requests.URLRequired` as a valid
exception, suggesting it would be raised for invalid URLs. However,
this exception has been dead code since commit ab27027 (2012) and is
never actually raised.

Instead, invalid URLs raise `MissingSchema`, `InvalidSchema`, or
`InvalidURL`, none of which were documented. This commit removes
`URLRequired` from the documentation to reflect the actual behavior
and prevent confusion.

Signed-off-by: jakobheine <me@jakobheine.de>
2025-02-01 19:42:06 +00:00
Nate Prewitt
fe0583ba49 Merge pull request #6875 from psf/dependabot/github_actions/github/codeql-action-3.28.5
Bump github/codeql-action from 3.27.0 to 3.28.5
2025-01-27 09:39:35 -08:00
dependabot[bot]
8c36da656d Bump github/codeql-action from 3.27.0 to 3.28.5
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.0 to 3.28.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](662472033e...f6091c0113)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 16:56:12 +00:00
anupam-arista
b653d80ded Merge branch 'psf:main' into patch-1 2024-12-13 05:08:45 +05:30
Ian Stapleton Cordasco
23540c93ca Merge pull request #6824 from psf/nateprewitt-patch-1
Remove old macOS runners
2024-11-10 10:18:36 -06:00
Nate Prewitt
ad959acfcf Remove old macOS runners 2024-11-09 22:50:30 -07:00
Nate Prewitt
a6cf27a77f Update vulnerability disclosure process (#6820)
* Update contact point for Vulnerability disclosures

* Fix RedHat Contact

* Point vulnerabilities.rst to our .SECURITY file
2024-11-02 13:13:02 -07:00
Nate Prewitt
ed0b1b5802 Merge pull request #6817 from psf/dependabot/github_actions/actions/setup-python-5.3.0
Bump actions/setup-python from 5.2.0 to 5.3.0
2024-10-28 10:24:23 -06:00
Nate Prewitt
1f16cffa43 Merge pull request #6818 from psf/dependabot/github_actions/github/codeql-action-3.27.0
Bump github/codeql-action from 3.26.0 to 3.27.0
2024-10-28 10:24:12 -06:00
dependabot[bot]
9787d0c09c Bump github/codeql-action from 3.26.0 to 3.27.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.0 to 3.27.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](eb055d739a...662472033e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 16:05:20 +00:00
dependabot[bot]
26664fa578 Bump actions/setup-python from 5.2.0 to 5.3.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](f677139bbe...0b93645e9f)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 16:05:11 +00:00
Nate Prewitt
7335bbf480 Merge pull request #6803 from psf/dependabot/github_actions/actions/checkout-4.2.0
Bump actions/checkout from 4.1.0 to 4.2.0
2024-09-30 10:32:21 -07:00
dependabot[bot]
0bff2d94e6 Bump actions/checkout from 4.1.0 to 4.2.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8ade135a41...d632683dd7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 16:34:20 +00:00
Ian Stapleton Cordasco
1ae6fc3137 Merge pull request #6802 from graingert/3.13
start testing on 3.13rc
2024-09-19 06:19:03 -05:00
Thomas Grainger
5984296b6a Update tox.ini 2024-09-18 19:40:02 +01:00
Thomas Grainger
f519bbae33 add trove classifier for 3.13 2024-09-18 19:14:19 +01:00
Thomas Grainger
314e7c9c70 upgrade to pytest-httpbin 2.1.0 final 2024-09-18 16:36:43 +01:00
Thomas Grainger
92f10cec5b upgrade to pytest-httpbin 2.1.0rc1 2024-09-18 15:04:53 +01:00
Thomas Grainger
83e67c4485 use allow-prereleases: true instead of 3.13-dev 2024-09-18 15:04:08 +01:00
Thomas Grainger
0f5ef1be8c Merge branch 'main' of github.com:psf/requests into 3.13 2024-09-18 15:03:08 +01:00
Nate Prewitt
f12ccbef6d Merge pull request #6797 from psf/dependabot/github_actions/actions/setup-python-5.2.0 2024-09-02 10:37:44 -07:00
dependabot[bot]
173890a48e Bump actions/setup-python from 5.1.0 to 5.2.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](82c7e631bb...f677139bbe)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 16:56:26 +00:00
Nate Prewitt
a3ce6f0075 Merge pull request #6787 from psf/dependabot/github_actions/github/codeql-action-3.26.0
Bump github/codeql-action from 3.25.0 to 3.26.0
2024-08-12 09:54:11 -07:00
dependabot[bot]
877892e67e Bump github/codeql-action from 3.25.0 to 3.26.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.0 to 3.26.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](df5a14dc28...eb055d739a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 16:23:16 +00:00
Nate Prewitt
79b74ef704 Merge pull request #6757 from nateprewitt/urllib-1.x-tests 2024-07-29 18:44:13 -07:00
Branch Vincent
15e1f17bb6 remove setuptools test command 2024-07-29 08:49:17 -05:00
Nate Prewitt
01353d3b4a Invert major version check 2024-07-23 04:28:03 -07:00
Nate Prewitt
4e383642a9 Add conditional string encoding based on urllib3 major version 2024-07-18 10:20:38 -07:00
Nate Prewitt
f8aa36b92d Test on urllib3 1.26.x 2024-07-01 18:08:04 -07:00
anupam-arista
06d2a38441 Merge branch 'psf:main' into patch-1 2024-07-01 10:46:41 +05:30
Nate Prewitt
0e322af877 v2.32.3
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.9) (push) Has been cancelled
Tests / build (macos-13, 3.8) (push) Has been cancelled
Tests / build (macos-13, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.9) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
2024-05-29 08:36:10 -07:00
Nate Prewitt
e188799322 Don't create default SSLContext if ssl module isn't present (#6724) 2024-05-29 08:23:39 -07:00
Nate Prewitt
145b539948 Merge pull request #6716 from sigmavirus24/bug/6715 2024-05-24 14:37:06 -07:00
Nate Prewitt
b1d73ddb50 Don't use default SSLContext with custom poolmanager kwargs 2024-05-24 11:38:10 -07:00
Nate Prewitt
6badbac6e0 Update HISTORY.md 2024-05-24 11:38:06 -07:00
Nate Prewitt
2e1452234d Start testing on 3.13 beta 2024-05-23 11:51:07 -07:00
Ian Stapleton Cordasco
a62a2d35d9 Allow for overriding of specific pool key params
This re-enables the use case of providing a custom SSLContext via a
Transport Adapter as broken in #6655 and reported in #6715

Closes #6715
2024-05-22 06:53:24 -05:00
Nate Prewitt
88dce9d854 v2.32.2
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.9) (push) Has been cancelled
Tests / build (macos-13, 3.8) (push) Has been cancelled
Tests / build (macos-13, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.9) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
2024-05-21 11:49:22 -07:00
Nate Prewitt
c98e4d133e Merge pull request #6710 from nateprewitt/api_rename
Move _get_connection to get_connection_with_tls_context
2024-05-21 09:41:18 -07:00
Nate Prewitt
92075b330a Add deprecation warning 2024-05-21 09:28:30 -07:00
Nate Prewitt
aa1461b68a Move _get_connection to get_connection_with_tls_context 2024-05-21 05:42:49 -07:00
Nate Prewitt
970e8cec98 v2.32.1
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.9) (push) Has been cancelled
Tests / build (macos-13, 3.8) (push) Has been cancelled
Tests / build (macos-13, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.9) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
2024-05-20 15:02:29 -07:00
Nate Prewitt
d6ebc4a2f1 v2.32.0
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.10) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.9) (push) Has been cancelled
Tests / build (macos-13, 3.8) (push) Has been cancelled
Tests / build (macos-13, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.10) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.9) (push) Has been cancelled
Tests / No Character Detection (push) Has been cancelled
2024-05-20 08:46:49 -07:00
agubelu
9a40d12778 Avoid reloading root certificates to improve concurrent performance (#6667) 2024-05-15 14:07:26 -06:00
Ian Stapleton Cordasco
0c030f78d2 Merge pull request #6702 from nateprewitt/no_char_detection
Allow optional char detection dependencies in post-packaging
2024-05-14 19:15:19 -05:00
Nate Prewitt
555b870eb1 Allow character detection dependencies to be optional in post-packaging steps 2024-05-14 15:26:04 -07:00
Ian Stapleton Cordasco
d6dded3f00 Merge pull request #6700 from franekmagiera/update-redirect-to-invalid-uri-test
Use an invalid URI that will not cause httpbin to throw 500
2024-05-12 16:33:47 -05:00
franekmagiera
bf24b7d8d1 Use an invalid URI that will not cause httpbin to throw 500 2024-05-12 22:08:24 +02:00
Nate Prewitt
2d5f54779a Pin 3.8 and 3.9 runners back to macos-13 (#6688) 2024-04-23 10:50:19 -07:00
Nate Prewitt
f1bb07d39b Merge pull request #6687 from psf/dependabot/github_actions/github/codeql-action-3.25.0
Bump github/codeql-action from 3.24.0 to 3.25.0
2024-04-15 11:01:39 -06:00
dependabot[bot]
60047ade64 Bump github/codeql-action from 3.24.0 to 3.25.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.0 to 3.25.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](e8893c57a1...df5a14dc28)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-15 16:40:35 +00:00
Ian Stapleton Cordasco
31ebb8102c Merge pull request #6682 from frenzymadness/pytest8
Fix compatibility with pytest 8
2024-04-11 14:49:35 -05:00
Lumir Balhar
cc23d1c644 Fix compatibility with pytest 8
`pytest.warns(None)` has been deprecated in pytest 7.0.0
and it's no longer working in pytest 8.

Resolves: https://github.com/psf/requests/issues/6679
2024-04-11 21:10:36 +02:00
Nate Prewitt
0790ea4250 Merge pull request #6680 from mbeijen/rfc9110
Add rfc9110 HTTP status code names
2024-04-11 07:10:57 -07:00
Michiel W. Beijen
e45b428960 Add rfc9110 HTTP status code names
RFC 9110 _HTTP Semantics_ obsoletes some earlier RFCs which defined HTTP
1.1. It adds some status codes that were previously only used for
WebDAV to HTTP _proper_ after making the names somewhat more generic.

See https://www.rfc-editor.org/rfc/rfc9110.html#name-changes-from-rfc-7231

This commit adds the http status code names from that RFC.
2024-04-08 21:48:24 +02:00
Nate Prewitt
2a438c27b5 Merge pull request #6677 from psf/dependabot/github_actions/actions/setup-python-5.1.0
Bump actions/setup-python from 5.0.0 to 5.1.0
2024-04-01 10:36:47 -07:00
dependabot[bot]
2daa7b52a7 Bump actions/setup-python from 5.0.0 to 5.1.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](0a5c615913...82c7e631bb)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 17:00:20 +00:00
Ian Stapleton Cordasco
8dd3b26bf5 Merge pull request #6302 from flysee/main
Fix the proxy_bypass_registry function all returning true in some cases.
2024-03-18 16:38:16 -05:00
flysee
13d892bdbe Additional should_bypass_proxies function test cases 2024-03-18 22:33:17 +08:00
flysee
1604e20fc8 Fix the proxy_bypass_registry function all returning true in some cases. 2024-03-18 15:36:36 +08:00
Ian Stapleton Cordasco
eeafb6ab3a Merge pull request #6662 from sigmavirus24/fix-tls-floppy
Add local TLS server
2024-03-14 06:25:24 -05:00
Ian Stapleton Cordasco
a94e9b5308 Add local TLS server
This also adds certificates for testing purposes and files to make it
easy to generate/regenerate them.

This also replaces an existing test of how we utilize our pool manager
such that we don't connect to badssl.com

Finally, this adds additional context parameters for our pool manager to
account for mTLS certificates used by clients to authenticate to a
server.
2024-03-14 06:06:22 -05:00
Ian Stapleton Cordasco
a58d7f2ffb Merge pull request #6655 from sigmavirus24/fix-tls-floppy
Use TLS settings in selecting connection pool
2024-03-11 06:21:59 -05:00
Ian Stapleton Cordasco
c0813a2d91 Use TLS settings in selecting connection pool
Previously, if someone made a request with `verify=False` then made a
request where they expected verification to be enabled to the same host,
they would potentially reuse a connection where TLS had not been
verified.

This fixes that issue.
2024-03-06 11:28:13 -06:00
Ian Stapleton Cordasco
eea3bbf9ac Merge pull request #6562 from amkarn258/issue-6223
every chardet package maps to requests.packages.chardet.* package respectively
2024-02-23 06:26:38 -06:00
Ian Stapleton Cordasco
f3f2611d83 Merge pull request #6583 from EFord36/fix-broken-links
Fix broken links in docs
2024-02-23 05:30:46 -06:00
Elliot Ford
a0e79bad05 update broken rfc link
ietf seems appropriate here - it's used elsewhere in the requets docs in
several places.
2024-02-23 10:26:21 +00:00
Elliot Ford
0a7f662aa7 Make example URL format a literal rather than an actual link
The rendered docs 'auto-link' this, which might encourage users to click
the link, even though it doesn't go anywhere.
2024-02-23 10:26:21 +00:00
Elliot Ford
5f1c3c22ff remove section with broken link to survey 2024-02-23 10:26:21 +00:00
Elliot Ford
541aa80ca3 update urllib3 docs link 2024-02-23 10:26:21 +00:00
Elliot Ford
d3b3399ece update authors github link
Update account link for original author which has changed.
2024-02-23 10:25:50 +00:00
Elliot Ford
0ec2780c29 update broken github pagination link 2024-02-23 10:25:50 +00:00
Ian Stapleton Cordasco
b8be93a721 Merge pull request #6589 from bruceadams/super_len_str_utf-8
Enhance `super_len` to count encoded bytes for str
2024-02-22 18:53:25 -06:00
Ian Stapleton Cordasco
3587a5f869 Merge pull request #6644 from sigmavirus24/bug/6643
Trim excess leading path separators
2024-02-22 18:49:42 -06:00
Ian Stapleton Cordasco
60389df6d6 Trim excess leading path separators
A URL with excess leading / (path-separator)s would cause urllib3 to
attempt to reparse the request-uri as a full URI with a host and port.
This bypasses that logic in ConnectionPool.urlopen by replacing these
leading /s with just a single /.

Closes #6643
2024-02-22 18:36:44 -06:00
Nate Prewitt
382fc2c0c6 Merge pull request #6629 from Tarty/fix-6628-jsondecode-error-not-deserializable
Fix #6628 - JSONDecodeError are not deserializable
2024-02-22 12:27:37 -08:00
Nate Prewitt
7a13c041db Merge pull request #6642 from nateprewitt/drop_python_37
Drop support for CPython 3.7
2024-02-20 15:44:43 -08:00
Nate Prewitt
58cea7a728 Drop support for CPython 3.7 2024-02-20 15:37:46 -08:00
Ian Stapleton Cordasco
9439fad038 Merge pull request #6641 from nateprewitt/fix_ci
Fix CI
2024-02-20 17:32:57 -06:00
Nate Prewitt
8fa4302322 Update Sphinx to work with latest readthedocs requirements 2024-02-20 15:04:38 -08:00
Nate Prewitt
28855fd43a Update supported copies of PyPy
Dropped support for pypy-3.7 and pypy-3.8 which are no longer maintained by upstream.
Added support for pypy-3.10.
2024-02-20 13:58:26 -08:00
Nate Prewitt
5fc10bf1f5 Fix httpbin pin for test suite 2024-02-20 13:37:25 -08:00
Ian Stapleton Cordasco
46c1a3d8c8 Merge pull request #6640 from nateprewitt/community_docs_cleanup
Cleanup defunct links from community docs page
2024-02-20 14:04:33 -06:00
Nate Prewitt
6106a63eb6 Cleanup defunct links from community docs page 2024-02-20 11:58:35 -08:00
Ian Stapleton Cordasco
4f3f189d6b Merge pull request #6632 from psf/dependabot/github_actions/github/codeql-action-3.24.0
Bump github/codeql-action from 3.23.0 to 3.24.0
2024-02-05 20:54:26 -06:00
dependabot[bot]
a5a0e4b587 Bump github/codeql-action from 3.23.0 to 3.24.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.23.0 to 3.24.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](e5f05b81d5...e8893c57a1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 16:38:01 +00:00
Thomas Dehghani
3ff3ff21dd Fix #6628 - JSONDecodeError are not deserializable
requests.exceptions.JSONDecodeError are not deserializable: calling
`pickle.dumps` followed by `pickle.loads` will trigger an error.

This is particularly a problem in a process pool, as an attempt to
decode json on an invalid json document will result in the entire
process pool crashing.

This is due to the MRO of the `requests.exceptions.JSONDecodeError`
class: the `__reduce__` method called when pickling an instance is not
the one from the JSON library parent: two out of three args expected
for instantiation will be dropped, and the instance can't be
deserialised.

By specifying in the class which parent `__reduce__` method should be
called, the bug is fixed as all args are carried over in the resulting
pickled bytes.
2024-01-31 17:13:53 +01:00
Ian Stapleton Cordasco
96b22fa18c Merge pull request #6619 from psf/dependabot/github_actions/github/codeql-action-3.23.0
Bump github/codeql-action from 3.22.11 to 3.23.0
2024-01-08 11:56:01 -06:00
dependabot[bot]
b0e6c9bf85 Bump github/codeql-action from 3.22.11 to 3.23.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.22.11 to 3.23.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](b374143c11...e5f05b81d5)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 17:01:37 +00:00
Ian Stapleton Cordasco
72eccc8dd8 Merge pull request #6612 from miketheman/fix-docs-urls
docs: specify sphinx dirhtml builder, use references instead of absolutes
2023-12-23 12:33:48 -06:00
Mike Fiedler
25939d8784 add myself
Signed-off-by: Mike Fiedler <miketheman@gmail.com>
2023-12-23 18:15:33 +00:00
Mike Fiedler
bfba9dc68c docs: replace concrete URLs with references
Any development links will now refer back to the generated docs.

Signed-off-by: Mike Fiedler <miketheman@gmail.com>
2023-12-23 18:09:55 +00:00
Mike Fiedler
f23346a9b8 Revert "Merge pull request #6605 from jaikishpai/fix-#6604"
This reverts commit 1396eb6f7a, reversing
changes made to e4c821a247.
2023-12-23 17:36:34 +00:00
Ian Stapleton Cordasco
f3f9784419 Merge pull request #6613 from miketheman/add-socks-label
docs: add label to socks heading
2023-12-23 11:24:56 -06:00
Mike Fiedler
b5bd0f14cc docs: add label to socks heading
When trying to link via intersphinx, a label must be used.
Otherwise a full URL is required, which is less desirable.

Signed-off-by: Mike Fiedler <miketheman@gmail.com>
2023-12-23 17:17:34 +00:00
Mike Fiedler
242d3113c0 docs: specify sphinx dirhtml builder
With the requirement of a configuration file, the default builder
of `dirhtml` that RTD used to use is no longer specified.
This leads to URLs ending in `.html` now, which
breaks other exisitng references.

Refs: #6603
Refs: https://docs.readthedocs.io/en/stable/config-file/v2.html#sphinx-builder
Refs: https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.dirhtml.DirectoryHTMLBuilder

Signed-off-by: Mike Fiedler <miketheman@gmail.com>
2023-12-23 16:16:50 +00:00
Nate Prewitt
1396eb6f7a Merge pull request #6605 from jaikishpai/fix-#6604
fix for ##6604
2023-12-19 22:17:55 -08:00
Nate Prewitt
e4c821a247 Merge pull request #6607 from psf/dependabot/github_actions/github/codeql-action-3.22.11
Bump github/codeql-action from 2.22.1 to 3.22.11
2023-12-18 09:30:42 -08:00
dependabot[bot]
421b1f1757 Bump github/codeql-action from 2.22.1 to 3.22.11
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.1 to 3.22.11.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](fdcae64e14...b374143c11)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 16:39:47 +00:00
Jaikish Pai
1447bccc05 fix for ##6604 2023-12-17 22:34:30 +01:00
Ian Stapleton Cordasco
3b5978fc5d Merge pull request #6592 from atatuzuner61/bug/6584
"TOO_EARLY" type definition for status code 425
2023-12-16 14:24:02 -06:00
Ian Stapleton Cordasco
1ddf014f47 Merge pull request #6600 from MestreLion/MestreLion-connection-timeout-note
Add note on connection timeout being larger than specified. Fix #5773
2023-12-16 07:30:18 -06:00
Ian Stapleton Cordasco
92f9e431c9 Update docs/user/advanced.rst
Add note about wall clock too
2023-12-16 07:29:50 -06:00
Ian Stapleton Cordasco
951dd15fa6 Update docs/user/advanced.rst
fix indentation for note so it renders properly
2023-12-16 07:26:23 -06:00
Ian Stapleton Cordasco
51d0d83eaf Merge pull request #6603 from nsoranzo/rtd_config
Add now mandatory readthedocs config file
2023-12-16 07:24:37 -06:00
Nicola Soranzo
c7c2ebf1a7 Add now mandatory readthedocs config file
Docs builds currently fail with:

```
Problem in your project's configuration. No default configuration file found at repository's root. See https://docs.readthedocs.io/en/stable/config-file/
```

See e.g. https://readthedocs.org/projects/requests/builds/22842479/
2023-12-15 20:59:56 +00:00
Rodrigo Silva
a64f32ba45 Add note on connection timeout being larger than specified. Fix #5773
On servers with multiple IPs, such as IPv4 and IPv6, `urllib3` tries each address sequentially until one successfully connects, using the specified timeout for _each_ attempt, leading to a total connection timeout that is a _multiple_ of the requested time.
2023-12-13 10:05:12 -03:00
Nate Prewitt
a25fde6989 Merge pull request #6599 from psf/dependabot/github_actions/actions/setup-python-5.0.0 2023-12-11 08:11:07 -08:00
dependabot[bot]
ba67dc8dcc Bump actions/setup-python from 4.7.0 to 5.0.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](61a6322f88...0a5c615913)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 16:07:15 +00:00
Nate Prewitt
769bc3ae5c Merge pull request #6596 from psf/fix-autoclose-automtaion
Update close-issues.yml
2023-12-01 09:18:02 -08:00
Ian Stapleton Cordasco
d6ffd868ee Update close-issues.yml
I noticed the auto-labeling was working but not the auto-closing. Looking at recent actions runs I see that we need to specify the token even if we're not giving our own special token. See https://github.com/psf/requests/actions/runs/7057701782/job/19211845073#step:2:13 for additional context, namely

```
 gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable. Example:
  env:
    GH_TOKEN: ${{ github.token }}
```
2023-12-01 08:05:59 -06:00
Bruce Adams
3fd309a5c1 Enhance super_len to count encoded bytes for str
This fixes issue #6586
2023-11-29 15:42:57 -05:00
Ata Tuzuner
ec84f2c539 Fixes to test 2023-11-29 12:27:18 -05:00
Ata Tuzuner
889910c77a Added tests for status code 425 definitions. 2023-11-29 12:24:15 -05:00
Ata Tuzuner
b37878d340 Too early definition added to 425 status code type 2023-11-28 18:12:22 -05:00
Bruce Adams
f6707042d8 Unit test for string containing multi-byte UTF-8
There are two tests here. One demonstrating existing, correct
behavior for `data=bytes`, and another, failing, test for the case
where `data=string` and the string contains multi-byte UTF-8.
2023-11-27 17:27:43 -05:00
Ian Stapleton Cordasco
0b4d494192 Merge pull request #6581 from EFord36/typo-fix
fix docstring typo: a -> as
2023-11-22 09:10:47 -06:00
Elliot Ford
15849947ec fix docstring typo: a -> as 2023-11-22 11:34:04 +00:00
Ian Stapleton Cordasco
c6de5a14e5 Merge pull request #6580 from psf/dependabot/github_actions/dessant/lock-threads-5.0.0
Bump dessant/lock-threads from 4.0.1 to 5.0.0
2023-11-20 10:37:07 -06:00
dependabot[bot]
e66a07b286 Bump dessant/lock-threads from 4.0.1 to 5.0.0
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 4.0.1 to 5.0.0.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md)
- [Commits](be8aa5be94...d42e5f4980)

---
updated-dependencies:
- dependency-name: dessant/lock-threads
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-20 16:28:17 +00:00
Nate Prewitt
9e98a8749a Merge pull request #6574 from msea1/matthew/fix_space 2023-11-15 23:44:49 -08:00
Matthew Carruth
c32b046243 Fix missing space in error message 2023-11-15 17:00:28 -08:00
mayank
89cde235be checkstyle 2023-11-04 23:20:21 +05:30
amkarn258
d7490c9d2d Update src/requests/packages.py
Co-authored-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
2023-10-31 22:36:39 +05:30
mayank
e9b1217cff added handling for chardet and charset_normalizer imports 2023-10-31 20:37:59 +05:30
mayank
ad761abcdd every chardet package maps to requests.packages.chardet.* package respectively 2023-10-31 00:50:10 +05:30
Nate Prewitt
839a8edec3 Merge pull request #6552 from swims-hjkl/issue/test_header_validation
#6551  - assert statements for test test_header_validation
2023-10-17 23:08:52 -07:00
sumedhrao7
a8e9c1b436 added assert statements into tests/test_requests/test_header_validation in regards to the issue #6551 2023-10-18 07:40:50 +05:30
Nate Prewitt
b55bb15c35 Merge pull request #6527 from sigmavirus24/update-templates
Autoclose specific issue templates
2023-10-11 09:29:20 -07:00
Ian Stapleton Cordasco
8187768622 Update close-issues.yml
Remove references to GITHUB_TOKEN/MY_TOKEN
2023-10-11 08:19:57 -05:00
Ian Stapleton Cordasco
f75b9504fc Update close comment
Co-authored-by: Nate Prewitt <nate.prewitt@gmail.com>
2023-10-11 08:19:14 -05:00
Nate Prewitt
8199a2b389 Merge pull request #6548 from psf/dependabot/github_actions/github/codeql-action-2.22.1
Bump github/codeql-action from 2.21.3 to 2.22.1
2023-10-09 09:49:28 -07:00
dependabot[bot]
42a3b5cce8 Bump github/codeql-action from 2.21.3 to 2.22.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.3 to 2.22.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](5b6282e01c...fdcae64e14)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-09 16:29:55 +00:00
Nate Prewitt
f5a7aefc2d Fix urllib3 pin in setup.cfg (#6545) 2023-10-06 16:34:24 -06:00
Nate Prewitt
ee93fac6b2 Merge pull request #6538 from psf/dependabot/github_actions/actions/checkout-4.1.0
Bump actions/checkout from 4.0.0 to 4.1.0
2023-09-25 11:30:05 -07:00
dependabot[bot]
a775435c4b Bump actions/checkout from 4.0.0 to 4.1.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](3df4ab11eb...8ade135a41)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 16:28:35 +00:00
anupam-arista
12862617df Update models.Response.json docstring clearer 2023-09-14 14:29:37 +05:30
Ian Stapleton Cordasco
6c8d9b1d0f Autoclose specific issue templates
We spend a fair amount of time closing issues because people don't read
the template closely or understand what they're being told. Let's take
advantage of being able to auto-label an issue based on the template and
then trigger a workflow to auto-magically close the issue with a message
and lock the issue.
2023-09-13 09:33:58 -05:00
Ian Stapleton Cordasco
881281250f Merge pull request #6519 from psf/dependabot/github_actions/actions/checkout-4.0.0
Bump actions/checkout from 3.6.0 to 4.0.0
2023-09-04 19:17:08 -05:00
dependabot[bot]
29fc8d1e93 Bump actions/checkout from 3.6.0 to 4.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](f43a0e5ff2...3df4ab11eb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 16:21:27 +00:00
Ian Stapleton Cordasco
8b560ecb24 Merge pull request #6517 from jnhyperion/remove-pytest-ini
Remove pytest.ini in MANIFEST.in since it does not exist anymore
2023-08-29 09:27:10 -05:00
Johnny.H
4585a7fbda remove pytest.ini since it does not exist anymore 2023-08-29 13:41:49 +08:00
Ian Stapleton Cordasco
16dc70df34 Merge pull request #6516 from psf/dependabot/github_actions/actions/checkout-3.6.0
Bump actions/checkout from 3.5.3 to 3.6.0
2023-08-28 14:05:56 -05:00
dependabot[bot]
bea231b033 Bump actions/checkout from 3.5.3 to 3.6.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](c85c95e3d7...f43a0e5ff2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 16:12:37 +00:00
13steinj
2ee5b0b01c Fix documentation monthly download badge (#6508) 2023-08-18 11:01:26 -06:00
Jonas Schell
89f0eb91b3 fix monthly download badge (#6507) 2023-08-16 08:16:53 -07:00
Nate Prewitt
005571d118 Remove pytest-mock requirement (#6505) 2023-08-13 16:08:21 -07:00
Nate Prewitt
d63e94f552 Move to src directory (#6506) 2023-08-13 14:46:13 -07:00
Nate Prewitt
e9fa2e2da3 Add 3.12 classifier and tox configuration (#6503) 2023-08-13 12:56:18 -07:00
Nate Prewitt
678fca8423 Upgrade to httpbin 0.10.0 (#6496) 2023-08-13 09:56:53 -07:00
Nate Prewitt
d8152769ce Merge pull request #6501 from psf/dependabot/github_actions/actions/checkout-3.5.3 2023-08-12 13:15:39 -07:00
Nate Prewitt
9ff1a25ef5 Merge pull request #6502 from psf/dependabot/github_actions/github/codeql-action-2.21.3
Bump github/codeql-action from 1.1.39 to 2.21.3
2023-08-12 12:50:52 -07:00
dependabot[bot]
4bd06cd325 Bump actions/checkout from 2.7.0 to 3.5.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.7.0 to 3.5.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.7.0...c85c95e3d7251135ab7dc9ce3241c5835cc595a9)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-12 19:37:22 +00:00
Nate Prewitt
09a241f59e Merge pull request #6500 from psf/dependabot/github_actions/actions/setup-python-4.7.0
Bump actions/setup-python from 2.3.4 to 4.7.0
2023-08-12 12:36:48 -07:00
Nate Prewitt
6ad493650c Merge pull request #6499 from psf/dependabot/github_actions/dessant/lock-threads-4.0.1
Bump dessant/lock-threads from 3.0.0 to 4.0.1
2023-08-12 12:24:59 -07:00
dependabot[bot]
9acca90bf6 Bump github/codeql-action from 1.1.39 to 2.21.3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.1.39 to 2.21.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](231aa2c8a8...5b6282e01c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-12 19:03:30 +00:00
dependabot[bot]
c7933453cf Bump actions/setup-python from 2.3.4 to 4.7.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.3.4 to 4.7.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2.3.4...61a6322f88396a6271a6ee3565807d608ecaddd1)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-12 19:03:22 +00:00
dependabot[bot]
ea49261a27 Bump dessant/lock-threads from 3.0.0 to 4.0.1
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 3.0.0 to 4.0.1.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md)
- [Commits](e460dfeb36...be8aa5be94)

---
updated-dependencies:
- dependency-name: dessant/lock-threads
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-12 19:03:17 +00:00
Nate Prewitt
8112fcc7be Pre commit update (#6498) 2023-08-12 12:03:10 -07:00
Nate Prewitt
2c193bda0c Pin GHA workflows and add dependabot to keep them up to date (#6497) 2023-08-12 12:02:58 -07:00
Ian Stapleton Cordasco
34a64bc65e Merge pull request #6266 from elprimato/spdx-conform-license
Change to SPDX conform license string
2023-08-12 13:51:42 -05:00
Nate Prewitt
9b6c62e235 Merge branch 'main' into spdx-conform-license 2023-08-12 11:37:15 -07:00
Joren Vrancken
cb7fcd7ebd Specify that Session.headers needs to be set to a OrderedDict in Header Ordering docs (#6475) 2023-08-12 10:53:36 -07:00
Calle Svensson
aa4cc78627 Add note about adapter prefix match to docs (#6465) 2023-07-29 21:05:44 -07:00
Kevin Kirsche
dfe46c1b7f refactor: prefer dictionary comphrension to loop (#6187) 2023-07-29 18:55:18 -07:00
Alexandre Erwin Ittner
2ecdb68561 Update reference to "cookielib" to "cookiejar" in documentation (#6214) 2023-07-29 18:50:43 -07:00
cpzt
fecf3dc472 add docstring parameter hooks (#6456) 2023-07-29 18:01:42 -07:00
Matthew Armand
cdbc2e2715 Add an Example for automatic retries to the Advanced Usage docs (#6258)
- While Requests doesn't automatically retry failures, this ability is a very common advanced use case in real world applications.
- Although there's a mention of this ability on the HTTPAdapter class docs, it's a bit buried and not very specific.
- It makes sense then to have an Example in the HTTPAdapter section of the Advanced Usage docs with a basic template for how this can be accomplished with Requests.
2023-07-03 13:38:22 -07:00
Boris Verkhovskiy
22db55a889 Fix doc typo (#6467) 2023-06-26 11:09:23 -06:00
Nate Prewitt
6e5b15d542 Fix linting issues 2023-05-22 09:36:22 -06:00
Nate Prewitt
147c8511dd v2.31.0
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.12-dev) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.8) (push) Has been cancelled
Tests / build (macOS-latest, pypy-3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.12-dev) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, pypy-3.9) (push) Has been cancelled
Tests / build (ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.12-dev) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.8) (push) Has been cancelled
Tests / build (windows-latest, pypy-3.9) (push) Has been cancelled
2023-05-22 09:10:32 -06:00
Nate Prewitt
74ea7cf7a6 Merge pull request from GHSA-j8r2-6x86-q33q 2023-05-22 09:08:57 -06:00
Thomas Grainger
3022253346 test on pypy 3.8 and pypy 3.9 on windows and macos (#6424) 2023-05-15 09:04:21 -06:00
Thomas Grainger
b639e66c81 test on py3.12 (#6448)
* test on py3.12

* update to pytest-httpbin==2.0.0
2023-05-13 08:10:56 -06:00
Syed Saifullah
d3d504436e Fixed a small typo (#6452) 2023-05-09 10:55:33 -06:00
Nate Prewitt
2ad18e0e10 v2.30.0
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (false, ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
2023-05-03 09:39:44 -06:00
Quentin Pradet
f2629e9e3c Remove strict parameter (#6434) 2023-04-28 13:17:10 -06:00
Nate Prewitt
87d63de873 v2.29.0
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (false, ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-22.04, 3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
2023-04-26 09:19:11 -06:00
Thomas Grainger
51716c4ef3 enable the warnings plugin (#6416) 2023-04-22 18:00:22 -06:00
Thomas Grainger
a7da1ab349 try on ubuntu 22.04 (#6418) 2023-04-22 17:53:09 -06:00
Thomas Grainger
dfc3e24338 pin cryptography<40 on pypy 3.7 (#6421)
* pin cryptography<40 on pypy 3.7
2023-04-22 17:41:27 -06:00
Levi
26bea1e498 Use urllib3 native chunking ability (#6226)
* Fix #3844 
* Use urllib for chunked 

---------

Co-authored-by: James Pickering <james.pickering@xml-solutions.com>
Co-authored-by: Leon Verrall <LVerrall@slb.com>
2023-04-22 17:32:51 -06:00
Seth Michael Larson
7f694b79e1 Allow str/bytes subclasses to be used as header parts 2023-03-03 14:39:02 -06:00
Yifang Zhu
e90852d20c Make json doc strings consistent (#6368) 2023-03-02 21:32:49 -08:00
Nate Prewitt
ec78348c4b Upgrade lint.yml action (#6370) 2023-03-02 20:45:27 -08:00
Boris Verkhovskiy
46188256ee Response.raw.read() returns bytes (#6365) 2023-02-27 08:31:15 -07:00
Quentin Pradet
15585909c3 Support missing SNIMissingWarning in tests (#6336) 2023-01-21 01:44:33 -07:00
Matthew Feickert
16a17a3ca7 fix: Remove '<4' from python_requires (#6333)
* As discussed in https://discuss.python.org/t/use-of-less-than-next-major-version-e-g-4-in-python-requires-setup-py/1066
  and other places by the PyPA, use of upper bounds with python_requires
  for future versions of Python is unintended use of python_requires and
  actively discouraged.
2023-01-18 22:37:24 -07:00
Nate Prewitt
61c324da43 v2.28.2
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (false, ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.11) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
2023-01-12 09:16:09 -07:00
Ashish Kurmi
eb07385b02 ci: add minimum GitHub token permissions for workflows (#6236)
Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>
2023-01-11 15:06:53 -07:00
Bruno Alla
2745db131a Switch to Python 3.11 final on CI and add to tox file (#6325) 2023-01-06 11:32:31 -07:00
ch-iv
ec553c275f Update flake8 repo and version (#6317) 2022-12-25 21:24:16 -07:00
Ian Stapleton Cordasco
ac3be98b19 Merge pull request #6291 from oliviacrain/docs/git-clone-protocol
Replace git protocol URL in docs with an HTTPS URL
2022-11-21 16:06:01 -06:00
Olivia Crain
192083576d Replace git protocol URL in docs with an HTTPS URL 2022-11-21 14:01:02 -06:00
Volker Schaus
53a0562331 change to SPDX conform license string 2022-10-23 18:35:43 +00:00
Ian Stapleton Cordasco
1e62a3ec18 Merge pull request #6262 from winmorre/docs-update/#6260
JSONDecodeError included in the documentation
2022-10-21 11:37:47 -05:00
reeko234
7908ad3af9 JSONDecodeError included in the documentation #6260 2022-10-21 07:42:58 +00:00
deedy5
c57f1f0ca1 Allow charset normalizer >=2 and <4 (#6261) 2022-10-20 12:26:18 -06:00
Ian Stapleton Cordasco
7104ad4b13 Merge pull request #6234 from jaap3/patch-1
Fix CURL_CA_BUNDLE override example
2022-09-09 14:10:14 -05:00
Jaap Roes
da246958b4 Fix CURL_CA_BUNDLE override example
Promote REQUESTS_CA_BUNDLE as it is looked at first
2022-09-07 09:48:59 +02:00
David Cain
177dd90f18 Remove Python 2 mention on chardet behavior (#6204) 2022-07-27 09:22:21 -08:00
Miloslav Pojman
786255613b Remove Python 2 mention from tox (#6200) 2022-07-20 17:13:36 -06:00
Sergey Lyapustin
bda7f0171f Fixed warnings message to suggest using secure protocol. (#6188) 2022-06-29 12:41:49 -06:00
Nate Prewitt
4d394574f5 v2.28.1
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (false, ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11-dev) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.11-dev) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11-dev) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
2022-06-29 09:09:11 -06:00
Kevin Kirsche
16b418b44a Use yield from instead of looping yield (#6170) 2022-06-28 20:08:26 -06:00
Lumír 'Frenzy' Balhar
0e28c8f1dc Allow charset normalizer >=2 and <3 (#6169) 2022-06-28 20:01:42 -06:00
Wong Hoi Sing Edison
73793ce43e Update Dependency with chardet>=3.0.2,<6 (#6179)
https://github.com/chardet/chardet/releases/tag/5.0.0 was released on
2022-06-25 with remove of Python 3.6 support, which also align with our
Python > 3.6 requirement.

Signed-off-by: Wong Hoi Sing Edison <hswong3i@pantarei-design.com>
2022-06-27 20:25:58 -06:00
Nate Prewitt
da9996fe4d v2.28.0
Some checks failed
Lint code / lint (push) Has been cancelled
Tests / build (false, ubuntu-latest, pypy-3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.10) (push) Has been cancelled
Tests / build (macOS-latest, 3.11-dev) (push) Has been cancelled
Tests / build (macOS-latest, 3.7) (push) Has been cancelled
Tests / build (macOS-latest, 3.8) (push) Has been cancelled
Tests / build (macOS-latest, 3.9) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.10) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.11-dev) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.7) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.8) (push) Has been cancelled
Tests / build (ubuntu-18.04, 3.9) (push) Has been cancelled
Tests / build (windows-latest, 3.10) (push) Has been cancelled
Tests / build (windows-latest, 3.11-dev) (push) Has been cancelled
Tests / build (windows-latest, 3.7) (push) Has been cancelled
Tests / build (windows-latest, 3.8) (push) Has been cancelled
Tests / build (windows-latest, 3.9) (push) Has been cancelled
2022-06-09 08:42:10 -06:00
Nate Prewitt
1dcf3b7122 Add GitHub action to automate linting (#6157) 2022-06-08 13:11:11 -06:00
Nate Prewitt
e36f34597c Add valdation for header name (#6154) 2022-06-08 12:03:56 -06:00
Nate Prewitt
60865f21ae Run 3.11 CI on all platforms (#6155) 2022-06-08 10:12:53 -06:00
Olivier Gayot
3af2f456d8 Fix output of test_lowlevel tests in case of timeout (#6136)
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
2022-06-08 10:02:42 -06:00
Nate Prewitt
0f358eabe6 Reduce lock thread runs to daily (#6150) 2022-06-01 11:51:04 -06:00
Nate Prewitt
210095fd08 Tolerate bad registry entries in Windows proxy settings (#6149) 2022-06-01 11:36:26 -06:00
Nate Prewitt
a5e7169e75 Cleanup the docs sidebar (#6144) 2022-05-26 12:45:57 -07:00
davidshivaji
79f2ec3acc Grammar fix (#6133) 2022-05-13 10:36:13 -06:00
Mickaël Schoentgen
cb233a101d Fix several ResourceWarnings in test_requests.py (#4766) 2022-05-10 19:04:17 -06:00
Nate Prewitt
40956723f2 Add .git-blame-ignore-revs 2022-04-29 15:39:22 -06:00
Nate Prewitt
2a6f290bc0 Add automatic code formatting to Requests (#6095) 2022-04-29 13:16:58 -06:00
Nate Prewitt
2d5517682b Fix inconsistent exception for JSONDecode error (#6097)
When calling .json() it was possible to raise a
requests.exceptions.JSONDecodeError or ValueError
depending on whether the encoding is set.
2022-03-28 12:51:27 -06:00
Nate Prewitt
8bce583b95 Farewell, sweet Concorde!
Formally removing any remaining vestiges of Python 2 from Requests.
We'll also leave behind Python 3.6 while we're at it.
2022-03-25 10:50:33 -06:00
Ian Stapleton Cordasco
79f60274f7 Merge pull request #6074 from nateprewitt/curl_ca_bundle
Fix environment CA Bundle resolution
2022-02-26 07:39:28 -06:00
Nate Prewitt
79c4a017fe Fix environment CA Bundle resolution 2022-02-25 11:25:20 -07:00
Ian Stapleton Cordasco
5e749546a2 Merge pull request #6068 from nateprewitt/sess_prox_warn
Reword proxies section and add warning
2022-02-19 17:04:06 -06:00
Ian Stapleton Cordasco
3eed0512a6 Merge pull request #6069 from nateprewitt/jinja_fix
Pin markupsafe for older versions of Jinja2
2022-02-19 07:34:48 -06:00
Nate Prewitt
3ed60078e2 Fix jinja 2022-02-18 18:33:43 -07:00
Nate Prewitt
15fde024f2 Reword proxies section and add warning 2022-02-18 18:18:23 -07:00
Nate Prewitt
95f4567336 Wrap urllib3's SSLError as requests' SSLError (#6057) 2022-02-05 11:56:05 -07:00
Nate Prewitt
7ae38876b1 Merge pull request #5978 from sha016/all-proxy-docs
Co-authored-by: Seth Michael Larson <sethmichaellarson@gmail.com>
2022-02-02 11:03:26 -07:00
Seth Michael Larson
2c20474bfe Update advanced.rst 2022-02-02 11:56:27 -06:00
Chavithra
24dfbfde55 Update quickstart.rst : fixing brotlicffi link (#6044) 2022-01-17 11:30:24 -07:00
chyzzqo2
fa1b0a367a Initialize JsonDecodeError before initializing IOError (#6036)
That way we get the formated error message

Co-authored-by: Peter <cHYzZQo@netflix.com>
2022-01-13 08:42:49 -07:00
Ian Stapleton Cordasco
d15a3b6e2e Merge pull request #6042 from nateprewitt/lock_thread_spam
Disable lock-threads on forks
2022-01-11 11:42:00 -06:00
Nate Prewitt
48676caf57 Disable lock-threads on forks 2022-01-11 08:24:39 -07:00
Nate Prewitt
d718e75383 Start testing on 3.11 dev branch (#6037) 2022-01-07 12:07:31 -07:00
Marwan
620ed4fc52 Move socks testing requirement into requirements-dev.txt (#6035) 2022-01-06 10:57:59 -07:00
sha016
e2fa8d3654 mention all_proxy env variable in Proxies section 2021-11-13 20:58:30 -06:00
120 changed files with 6888 additions and 4391 deletions

View File

@@ -1,2 +1,2 @@
[run]
omit = requests/packages/*
omit = requests/packages/*

5
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,5 @@
# You can configure git to automatically use this file with the following config:
# git config --global blame.ignoreRevsFile .git-blame-ignore-revs
# Add automatic code formatting to Requests
2a6f290bc09324406708a4d404a88a45d848ddf9

8
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,8 @@
# Restrict all files related to deploying to
# require lead maintainer approval.
.github/workflows/ @nateprewitt @sigmavirus24
.github/CODEOWNERS @nateprewitt @sigmavirus24
src/requests/__version__.py @nateprewitt @sigmavirus24
HISTORY.md @nateprewitt @sigmavirus24
pyproject.toml @nateprewitt @sigmavirus24

View File

@@ -25,4 +25,4 @@ import requests
This command is only available on Requests v2.16.4 and greater. Otherwise,
please provide some basic information about your system (Python version,
operating system, &c).
operating system, &c).

View File

@@ -1,6 +1,9 @@
---
name: Request for Help
about: Guidance on using Requests.
labels:
- "Question/Not a bug"
- "actions/autoclose-qa"
---

View File

@@ -1,6 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
labels:
- "Feature Request"
- "actions/autoclose-feat"
---

17
.github/SECURITY.md vendored
View File

@@ -1,18 +1,9 @@
# Vulnerability Disclosure
If you think you have found a potential security vulnerability in
requests, please email [Nate](mailto:nate.prewitt@gmail.com)
and [Seth](mailto:sethmichaellarson@gmail.com) directly.
**Do not file a public issue.**
Our PGP Key fingerprints are:
- 8722 7E29 AD9C FF5C FAC3 EA6A 44D3 FF97 B80D C864 ([@nateprewitt](https://keybase.io/nateprewitt))
- EDD5 6765 A9D8 4653 CBC8 A134 51B0 6736 1740 F5FC ([@sethmlarson](https://keybase.io/sethmlarson))
You can also contact us on [Keybase](https://keybase.io) with the
profiles above if desired.
requests, please open a [draft Security Advisory](https://github.com/psf/requests/security/advisories/new)
via GitHub. We will coordinate verification and next steps through
that secure medium.
If English is not your first language, please try to describe the
problem and its impact to the best of your ability. For greater detail,
@@ -72,7 +63,7 @@ intended patch ahead of time, to ensure that they are able to promptly
release their downstream packages. Currently the list of people we
actively contact *ahead of a public release* is:
- Jeremy Cline, Red Hat (@jeremycline)
- Python Maintenance Team, Red Hat (python-maint@redhat.com)
- Daniele Tricoli, Debian (@eriol)
We will notify these individuals at least a week ahead of our planned

27
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
ignore:
# Ignore all patch releases as we can manually
# upgrade if we run into a bug and need a fix.
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
cooldown:
default-days: 7
groups:
actions:
patterns:
- "*"
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
groups:
pre-commit:
patterns:
- "*"

38
.github/workflows/close-issues.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: 'Autoclose Issues'
on:
issues:
types:
- labeled
permissions: {}
jobs:
close_qa:
permissions:
issues: write
if: github.event.label.name == 'actions/autoclose-qa'
runs-on: ubuntu-latest
steps:
- env:
ISSUE_URL: ${{ github.event.issue.html_url }}
GH_TOKEN: ${{ github.token }}
run: |
gh issue close $ISSUE_URL \
--comment "As described in the template, we won't be able to answer questions on this issue tracker. Please use [Stack Overflow](https://stackoverflow.com/)" \
--reason completed
gh issue lock $ISSUE_URL --reason off_topic
close_feature_request:
permissions:
issues: write
if: github.event.label.name == 'actions/autoclose-feat'
runs-on: ubuntu-latest
steps:
- env:
ISSUE_URL: ${{ github.event.issue.html_url }}
GH_TOKEN: ${{ github.token }}
run: |
gh issue close $ISSUE_URL \
--comment "As described in the template, Requests is not accepting feature requests" \
--reason "not planned"
gh issue lock $ISSUE_URL --reason off_topic

View File

@@ -14,8 +14,15 @@ on:
schedule:
- cron: '0 23 * * 0'
permissions:
contents: read
jobs:
analyze:
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/autobuild to send a status report
name: Analyze
runs-on: ubuntu-latest
@@ -25,11 +32,12 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
persist-credentials: false
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
@@ -38,18 +46,18 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
languages: "python"
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -63,4 +71,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1

22
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Lint code
on: [push, pull_request]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.x"
- name: Run pre-commit
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # v3.0.0

View File

@@ -2,7 +2,7 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 * * * *'
- cron: '0 0 * * *'
permissions:
issues: write
@@ -10,9 +10,10 @@ permissions:
jobs:
action:
if: github.repository_owner == 'psf'
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
- uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
with:
issue-lock-inactive-days: 90
pr-lock-inactive-days: 90

95
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: Publish to PyPI
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
test-pypi-only:
description: "Publish to Test PyPI only"
type: boolean
default: true
permissions:
contents: read
jobs:
build:
name: "Build dists"
runs-on: "ubuntu-latest"
outputs:
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
steps:
- name: "Checkout repository"
uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2
with:
persist-credentials: false
- name: "Setup Python"
uses: "actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405" # v6.2.0
with:
python-version: "3.x"
- name: "Install dependencies"
run: python -m pip install build==1.4.0
- name: "Build dists"
run: |
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \
python -m build
- name: "Upload dists"
uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"
id: upload-artifact
with:
name: "dist"
path: "dist/"
if-no-files-found: error
retention-days: 5
publish:
name: "Publish"
if: startsWith(github.ref, 'refs/tags/')
needs: ["build"]
permissions:
id-token: write
runs-on: "ubuntu-latest"
environment:
name: "publish"
steps:
- name: "Download dists"
uses: "actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3" # v8.0.0
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: "dist/"
- name: "Publish dists to PyPI"
uses: "pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b" # v1.14.0
with:
attestations: true
publish-test-pypi:
name: "Publish to Test PyPI"
if: github.event_name == 'workflow_dispatch'
needs: ["build"]
permissions:
id-token: write
runs-on: "ubuntu-latest"
environment:
name: "testpypi"
steps:
- name: "Download dists"
uses: "actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3" # v8.0.0
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: "dist/"
- name: "Publish dists to Test PyPI"
uses: "pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b" # v1.14.0
with:
repository-url: https://test.pypi.org/legacy/
attestations: true

View File

@@ -2,32 +2,83 @@ name: Tests
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.python-version == '3.15-dev' }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10"]
os: [ubuntu-18.04, macOS-latest, windows-latest]
include:
# pypy3 on Mac OS currently fails trying to compile
# brotlipy. Moving pypy3 to only test linux.
- python-version: pypy3
os: ubuntu-latest
- python-version: pypy-3.7
os: ubuntu-latest
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15-dev", "pypy-3.11"]
os: [ubuntu-22.04, macOS-latest, windows-latest]
# Pypy-3.11 can't install openssl-sys with rust
# which prevents us from testing in GHA.
exclude:
- { python-version: "pypy-3.11", os: "windows-latest" }
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
allow-prereleases: true
- name: Install dependencies
env:
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: ${{ matrix.python-version == '3.15-dev' && '1' || '' }}
run: |
make
- name: Run tests
run: |
make ci
no_chardet:
name: "No Character Detection"
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: 'Set up Python 3.10'
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: '3.10'
- name: Install dependencies
run: |
make
python -m pip uninstall -y "charset_normalizer" "chardet"
- name: Run tests
run: |
make ci
urllib3:
name: 'urllib3 1.x'
runs-on: 'ubuntu-latest'
strategy:
fail-fast: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: 'Set up Python 3.10'
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: '3.10'
- name: Install dependencies
run: |
make
python -m pip install "urllib3<2"
- name: Run tests
run: |
make ci

32
.github/workflows/typecheck.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Type Check
on: [push, pull_request]
permissions:
contents: read
jobs:
typecheck:
runs-on: ubuntu-24.04
timeout-minutes: 10
strategy:
matrix:
python-version: ["3.10", "3.14"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install pip==26.0.1
python -m pip install -e . --group typecheck
- name: Run pyright
run: python -m pyright src/requests/

24
.github/workflows/zizmor.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
# Sourced from https://github.com/zizmorcore/zizmor-action
name: GitHub Actions Security Analysis with zizmor 🌈
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
permissions: {}
jobs:
zizmor:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2

21
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,21 @@
exclude: 'docs/|ext/'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-case-conflict
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
args: [--fix=auto]
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
exclude: tests/test_lowlevel.py

29
.readthedocs.yaml Normal file
View File

@@ -0,0 +1,29 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.12"
# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/conf.py
builder: "dirhtml"
# Optionally build your docs in additional formats such as PDF and ePub
formats:
- pdf
- epub
# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- path: .
- requirements: docs/requirements.txt

View File

@@ -1,17 +1,17 @@
Requests was lovingly created by Kenneth Reitz.
Keepers of the Crystals
```````````````````````
Requests Maintainers
````````````````````
- Ian Stapleton Cordasco <graffatcolmingov@gmail.com> `@sigmavirus24 <https://github.com/sigmavirus24>`_.
- Nate Prewitt `@nateprewitt <https://github.com/nateprewitt>`_.
- Seth M. Larson `@sethmlarson <https://github.com/sethmlarson>`_.
Previous Keepers of Crystals
````````````````````````````
- Kenneth Reitz <me@kennethreitz.org> `@ken-reitz <https://github.com/ken-reitz>`_, reluctant Keeper of the Master Crystal.
Previous Maintainers
````````````````````
- Kenneth Reitz <me@kennethreitz.org> `@kennethreitz <https://github.com/kennethreitz>`_, reluctant Keeper of the Master Crystal.
- Cory Benfield <cory@lukasa.co.uk> `@lukasa <https://github.com/lukasa>`_
- Ian Cordasco <graffatcolmingov@gmail.com> `@sigmavirus24 <https://github.com/sigmavirus24>`_.
- Seth M. Larson `@sethmlarson <https://github.com/sethmlarson>`_.
Patches and Suggestions
```````````````````````
@@ -192,3 +192,4 @@ Patches and Suggestions
- Alessio Izzo (`@aless10 <https://github.com/aless10>`_)
- Sylvain Marié (`@smarie <https://github.com/smarie>`_)
- Hod Bin Noon (`@hodbn <https://github.com/hodbn>`_)
- Mike Fiedler (`@miketheman <https://github.com/miketheman>`_)

View File

@@ -6,6 +6,264 @@ dev
- \[Short description of non-trivial change.\]
2.34.0 (2026-05-??)
-------------------
**Improvements**
* Requests 2.34.0 introduces inline types, replacing those provided by
typeshed. Public API types should be fully compatible with mypy, pyright,
and ty. (#7272)
* Digest Auth hashing algorithms have added `usedforsecurity=False` to clarify
security considerations. (#7310)
**Bugfixes**
* ``Response.history`` no longer contains a reference to itself, preventing
accidental looping when traversing the history list. (#7328)
2.33.1 (2026-03-30)
-------------------
**Bugfixes**
- Fixed test cleanup for CVE-2026-25645 to avoid leaving unnecessary
files in the tmp directory. (#7305)
- Fixed Content-Type header parsing for malformed values. (#7309)
- Improved error consistency for malformed header values. (#7308)
2.33.0 (2026-03-25)
-------------------
**Announcements**
- 📣 Requests is adding inline types. If you have a typed code base that
uses Requests, please take a look at #7271. Give it a try, and report
any gaps or feedback you may have in the issue. 📣
**Security**
- CVE-2026-25645 ``requests.utils.extract_zipped_paths`` now extracts
contents to a non-deterministic location to prevent malicious file
replacement. This does not affect default usage of Requests, only
applications calling the utility function directly.
**Improvements**
- Migrated to a PEP 517 build system using setuptools. (#7012)
**Bugfixes**
- Fixed an issue where an empty netrc entry could cause
malformed authentication to be applied to Requests on
Python 3.11+. (#7205)
**Deprecations**
- Dropped support for Python 3.9 following its end of support. (#7196)
**Documentation**
- Various typo fixes and doc improvements.
2.32.5 (2025-08-18)
-------------------
**Bugfixes**
- The SSLContext caching feature originally introduced in 2.32.0 has created
a new class of issues in Requests that have had negative impact across a number
of use cases. The Requests team has decided to revert this feature as long term
maintenance of it is proving to be unsustainable in its current iteration.
**Deprecations**
- Added support for Python 3.14.
- Dropped support for Python 3.8 following its end of support.
2.32.4 (2025-06-10)
-------------------
**Security**
- CVE-2024-47081 Fixed an issue where a maliciously crafted URL and trusted
environment will retrieve credentials for the wrong hostname/machine from a
netrc file.
**Improvements**
- Numerous documentation improvements
**Deprecations**
- Added support for pypy 3.11 for Linux and macOS.
- Dropped support for pypy 3.9 following its end of support.
2.32.3 (2024-05-29)
-------------------
**Bugfixes**
- Fixed bug breaking the ability to specify custom SSLContexts in sub-classes of
HTTPAdapter. (#6716)
- Fixed issue where Requests started failing to run on Python versions compiled
without the `ssl` module. (#6724)
2.32.2 (2024-05-21)
-------------------
**Deprecations**
- To provide a more stable migration for custom HTTPAdapters impacted
by the CVE changes in 2.32.0, we've renamed `_get_connection` to
a new public API, `get_connection_with_tls_context`. Existing custom
HTTPAdapters will need to migrate their code to use this new API.
`get_connection` is considered deprecated in all versions of Requests>=2.32.0.
A minimal (2-line) example has been provided in the linked PR to ease
migration, but we strongly urge users to evaluate if their custom adapter
is subject to the same issue described in CVE-2024-35195. (#6710)
2.32.1 (2024-05-20)
-------------------
**Bugfixes**
- Add missing test certs to the sdist distributed on PyPI.
2.32.0 (2024-05-20)
-------------------
**Security**
- Fixed an issue where setting `verify=False` on the first request from a
Session will cause subsequent requests to the _same origin_ to also ignore
cert verification, regardless of the value of `verify`.
(https://github.com/psf/requests/security/advisories/GHSA-9wx4-h78v-vm56)
**Improvements**
- `verify=True` now reuses a global SSLContext which should improve
request time variance between first and subsequent requests. It should
also minimize certificate load time on Windows systems when using a Python
version built with OpenSSL 3.x. (#6667)
- Requests now supports optional use of character detection
(`chardet` or `charset_normalizer`) when repackaged or vendored.
This enables `pip` and other projects to minimize their vendoring
surface area. The `Response.text()` and `apparent_encoding` APIs
will default to `utf-8` if neither library is present. (#6702)
**Bugfixes**
- Fixed bug in length detection where emoji length was incorrectly
calculated in the request content-length. (#6589)
- Fixed deserialization bug in JSONDecodeError. (#6629)
- Fixed bug where an extra leading `/` (path separator) could lead
urllib3 to unnecessarily reparse the request URI. (#6644)
**Deprecations**
- Requests has officially added support for CPython 3.12 (#6503)
- Requests has officially added support for PyPy 3.9 and 3.10 (#6641)
- Requests has officially dropped support for CPython 3.7 (#6642)
- Requests has officially dropped support for PyPy 3.7 and 3.8 (#6641)
**Documentation**
- Various typo fixes and doc improvements.
**Packaging**
- Requests has started adopting some modern packaging practices.
The source files for the projects (formerly `requests`) is now located
in `src/requests` in the Requests sdist. (#6506)
- Starting in Requests 2.33.0, Requests will migrate to a PEP 517 build system
using `hatchling`. This should not impact the average user, but extremely old
versions of packaging utilities may have issues with the new packaging format.
2.31.0 (2023-05-22)
-------------------
**Security**
- Versions of Requests between v2.3.0 and v2.30.0 are vulnerable to potential
forwarding of `Proxy-Authorization` headers to destination servers when
following HTTPS redirects.
When proxies are defined with user info (`https://user:pass@proxy:8080`), Requests
will construct a `Proxy-Authorization` header that is attached to the request to
authenticate with the proxy.
In cases where Requests receives a redirect response, it previously reattached
the `Proxy-Authorization` header incorrectly, resulting in the value being
sent through the tunneled connection to the destination server. Users who rely on
defining their proxy credentials in the URL are *strongly* encouraged to upgrade
to Requests 2.31.0+ to prevent unintentional leakage and rotate their proxy
credentials once the change has been fully deployed.
Users who do not use a proxy or do not supply their proxy credentials through
the user information portion of their proxy URL are not subject to this
vulnerability.
Full details can be read in our [Github Security Advisory](https://github.com/psf/requests/security/advisories/GHSA-j8r2-6x86-q33q)
and [CVE-2023-32681](https://nvd.nist.gov/vuln/detail/CVE-2023-32681).
2.30.0 (2023-05-03)
-------------------
**Dependencies**
- ⚠️ Added support for urllib3 2.0. ⚠️
This may contain minor breaking changes so we advise careful testing and
reviewing https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html
prior to upgrading.
Users who wish to stay on urllib3 1.x can pin to `urllib3<2`.
2.29.0 (2023-04-26)
-------------------
**Improvements**
- Requests now defers chunked requests to the urllib3 implementation to improve
standardization. (#6226)
- Requests relaxes header component requirements to support bytes/str subclasses. (#6356)
2.28.2 (2023-01-12)
-------------------
**Dependencies**
- Requests now supports charset\_normalizer 3.x. (#6261)
**Bugfixes**
- Updated MissingSchema exception to suggest https scheme rather than http. (#6188)
2.28.1 (2022-06-29)
-------------------
**Improvements**
- Speed optimization in `iter_content` with transition to `yield from`. (#6170)
**Dependencies**
- Added support for chardet 5.0.0 (#6179)
- Added support for charset-normalizer 2.1.0 (#6169)
2.28.0 (2022-06-09)
-------------------
**Deprecations**
- ⚠️ Requests has officially dropped support for Python 2.7. ⚠️ (#6091)
- Requests has officially dropped support for Python 3.6 (including pypy3.6). (#6091)
**Improvements**
- Wrap JSON parsing issues in Request's JSONDecodeError for payloads without
an encoding to make `json()` API consistent. (#6097)
- Parse header components consistently, raising an InvalidHeader error in
all invalid cases. (#6154)
- Added provisional 3.11 support with current beta build. (#6155)
- Requests got a makeover and we decided to paint it black. (#6095)
**Bugfixes**
- Fixed bug where setting `CURL_CA_BUNDLE` to an empty string would disable
cert verification. All Requests 2.x versions before 2.28.0 are affected. (#6074)
- Fixed urllib3 exception leak, wrapping `urllib3.exceptions.SSLError` with
`requests.exceptions.SSLError` for `content` and `iter_content`. (#6057)
- Fixed issue where invalid Windows registry entries caused proxy resolution
to raise an exception rather than ignoring the entry. (#6149)
- Fixed issue where entire payload could be included in the error message for
JSONDecodeError. (#6036)
2.27.1 (2022-01-05)
-------------------

View File

@@ -1,2 +1,3 @@
include README.md LICENSE NOTICE HISTORY.md pytest.ini requirements-dev.txt
include README.md LICENSE NOTICE HISTORY.md requirements-dev.txt
recursive-include tests *.py
recursive-include tests/certs *

View File

@@ -1,26 +1,25 @@
.PHONY: docs
init:
pip install -e .[socks]
pip install -r requirements-dev.txt
python -m pip install -r requirements-dev.txt
test:
# This runs all of the tests, on both Python 2 and Python 3.
tox -p
python -m pytest tests
ci:
pytest tests --junitxml=report.xml
python -m pytest tests --junitxml=report.xml
test-readme:
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
coverage:
pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=requests tests
python -m pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=src/requests tests
publish:
pip install 'twine>=1.5.0'
python setup.py sdist bdist_wheel
twine upload dist/*
.publishenv:
python -m venv .publishenv
.publishenv/bin/pip install 'twine>=1.5.0' build
publish: .publishenv
.publishenv/bin/python -m build
.publishenv/bin/python -m twine upload --skip-existing dist/*
rm -fr build dist .egg requests.egg-info
docs:

View File

@@ -1,5 +1,11 @@
# Requests
[![Version](https://img.shields.io/pypi/v/requests.svg?maxAge=86400)](https://pypi.org/project/requests/)
[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests)
[![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests)
[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors)
[![Documentation](https://readthedocs.org/projects/requests/badge/?version=latest)](https://requests.readthedocs.io)
**Requests** is a simple, yet elegant, HTTP library.
```python
@@ -19,11 +25,7 @@
Requests allows you to send HTTP/1.1 requests extremely easily. Theres no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method!
Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code.
[![Downloads](https://pepy.tech/badge/requests/month)](https://pepy.tech/project/requests)
[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests)
[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors)
Requests is one of the most downloaded Python packages today, pulling in around `300M downloads / week` — according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `4,000,000+` repositories.
## Installing Requests and Supported Versions
@@ -33,7 +35,7 @@ Requests is available on PyPI:
$ python -m pip install requests
```
Requests officially supports Python 2.7 & 3.6+.
Requests officially supports Python 3.10+.
## Supported Features & BestPractices
@@ -53,14 +55,10 @@ Requests is ready for the demands of building robust and reliable HTTPspeakin
- Automatic honoring of `.netrc`
- Chunked HTTP Requests
## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io)
[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io)
## Cloning the repository
When cloning the Requests repository, you may need to add the `-c
fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see
fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit timestamp (see
[this issue](https://github.com/psf/requests/issues/2690) for more background):
```shell

View File

@@ -1,177 +1,12 @@
body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-child(2) > td > select {
width: 100%!important;
}
#python27 > a {
color: white;
}
/* Carbon by BuySellAds */
#carbonads {
display: block;
overflow: hidden;
margin: 1.5em 0 2em;
padding: 1em;
border: solid 1px #cccccc;
border-radius: 2px;
background-color: #eeeeee;
text-align: center;
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;
font-size: 10px;
line-height: 1;
}
/* Native CPC by BuySellAds */
#native-ribbon #_custom_ {
position: fixed;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0 -1px 4px 1px hsla(0, 0%, 0%, .15);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
transition: all .25s ease-in-out;
transform: translateY(calc(100% - 35px));
flex-flow: column nowrap;
}
#native-ribbon #_custom_:hover {
transform: translateY(0);
}
.native-img {
margin-right: 20px;
max-height: 50px;
border-radius: 3px;
}
.native-sponsor {
margin: 10px 20px;
text-align: center;
text-transform: uppercase;
letter-spacing: .5px;
font-size: 12px;
transition: all .3s ease-in-out;
transform-origin: left;
}
#native-ribbon #_custom_:hover .native-sponsor {
margin: 0 20px;
opacity: 0;
transform: scaleY(0);
}
.native-flex {
display: flex;
padding: 10px 20px 25px;
text-decoration: none;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
}
.native-main {
display: flex;
flex-flow: row nowrap;
align-items: center;
}
.native-details {
display: flex;
margin-right: 30px;
flex-flow: column nowrap;
}
.native-company {
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 10px;
}
.native-desc {
letter-spacing: 1px;
font-weight: 300;
font-size: 14px;
line-height: 1.4;
}
.native-cta {
padding: 10px 14px;
border-radius: 3px;
box-shadow: 0 6px 13px 0 hsla(0, 0%, 0%, .15);
text-transform: uppercase;
white-space: nowrap;
letter-spacing: 1px;
font-weight: 400;
font-size: 12px;
transition: all .3s ease-in-out;
transform: translateY(-1px);
}
.native-cta:hover {
box-shadow: none;
transform: translateY(1px);
}
@media only screen and (min-width: 320px) and (max-width: 759px) {
.native-flex {
padding: 5px 5px 15px;
flex-direction: column;
flex-wrap: wrap;
}
.native-img {
margin: 0;
display: none;
}
.native-details {
margin: 0;
}
.native-main {
flex-direction: column;
text-align: left;
flex-wrap: wrap;
align-content: center;
}
.native-cta {
display: none;
}
img.logo {margin-left: -20px!important;}
div#searchbox h3 {text-transform: capitalize;}
div.document {width: 1008px;}
div.highlight pre {padding: 11px 14px;}
@media screen and (max-width: 1008px) {
div.document {width: 100%!important;}
div.highlight pre {margin-right: -30px;}
}

View File

@@ -1,80 +0,0 @@
<!-- Alabaster (krTheme++) Hacks -->
<!-- CSS Adjustments (I'm very picky.) -->
<style type="text/css">
/* Rezzy requires precise alignment. */
img.logo {margin-left: -20px!important;}
/* "Quick Search" should be capitalized. */
div#searchbox h3 {text-transform: capitalize;}
/* Make the document a little wider, less code is cut-off. */
div.document {width: 1008px;}
/* Much-improved spacing around code blocks. */
div.highlight pre {padding: 11px 14px;}
/* Remain Responsive! */
@media screen and (max-width: 1008px) {
div.sphinxsidebar {display: none;}
div.document {width: 100%!important;}
/* Have code blocks escape the document right-margin. */
div.highlight pre {margin-right: -30px;}
}
</style>
<!-- Native CPC by BuySellAds -->
<script type="text/javascript" src="//m.servedby-buysellads.com/monetization.js"></script>
<div id="native-ribbon">
</div>
<script>
_bsa.init('custom', 'CK7D62JU', 'placement:pythonrequestsorg',
{
target: '#native-ribbon',
template: `
<div class="native-sponsor">Sponsored by ##company## — Learn More</div>
<a href="##link##" class="native-flex">
<style>
#native-ribbon #_custom_ {
background: linear-gradient(-30deg, ##backgroundColor##E5, ##backgroundColor##E5 45%, ##backgroundColor## 45%) #fff;
}
.native-details,
.native-sponsor,
.native-bsa {
color: ##textColor## !important;
}
.native-details:hover {
color: ##textColorHover## !important;
}
.native-cta {
color: ##ctaTextColor##;
background-color: ##ctaBackgroundColor##;
}
.native-cta:hover {
color: ##ctaTextColorHover##;
background-color: ##ctaBackgroundColorHover##;
}
</style>
<div class="native-main">
<img class="native-img" src="##logo##">
<div class="native-details">
<span class="native-company">##title##</span>
<span class="native-desc">##description##</span>
</div>
</div>
<span class="native-cta">##callToAction##</span>
</a>
</div>
`
}
);
</script>

33
docs/_templates/sidebar.html vendored Normal file
View File

@@ -0,0 +1,33 @@
<p class="logo">
<a href="{{ pathto(root_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="{{ _('Requests logo') }}" />
</a>
</p>
<p>
<iframe src="https://ghbtns.com/github-btn.html?user=psf&repo=requests&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
</p>
<p>
{{ _('Requests is an elegant and simple HTTP library for Python, built for human beings.') }}
</p>
<h3>{{ _('Useful Links') }}</h3>
<ul>
<li><a href="{{ pathto('user/quickstart') }}">{{ _('Quickstart') }}</a></li>
<li><a href="{{ pathto('user/advanced') }}">{{ _('Advanced Usage') }}</a></li>
<li><a href="{{ pathto('api') }}">{{ _('API Reference') }}</a></li>
<li><a href="{{ pathto('community/updates') + '#release-history' }}">{{ _('Release History') }}</a></li>
<li><a href="{{ pathto('dev/contributing') }}">{{ _('Contributors Guide') }}</a></li>
<p></p>
<li><a href="{{ pathto('community/recommended') }}">{{ _('Recommended Packages and Extensions') }}</a></li>
<p></p>
<li><a href="https://github.com/psf/requests">{{ _('Requests @ GitHub') }}</a></li>
<li><a href="https://pypi.org/project/requests/">{{ _('Requests @ PyPI') }}</a></li>
<li><a href="https://github.com/psf/requests/issues">{{ _('Issue Tracker') }}</a></li>
</ul>

View File

@@ -1,79 +0,0 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Requests logo" />
</a>
</p>
<p>
<iframe src="https://ghbtns.com/github-btn.html?user=psf&repo=requests&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
</p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
<style>
.algolia-autocomplete {
width: 100%;
height: 1.5em
}
.algolia-autocomplete a {
border-bottom: none !important;
}
#doc_search {
width: 100%;
height: 100%;
}
</style>
<input id="doc_search" placeholder="Search the doc" autofocus />
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js" onload="docsearch({
apiKey: 'f177061e2354c50a97bfc635e827ffab',
indexName: 'python-requests',
inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown
})" async></script>
<p>
Requests is an elegant and simple HTTP library for Python, built for
human beings.
</p>
<p>Sponsored by <strong><a href="https://www.govcert.lu">CERT Gouvernemental - GOVCERT.LU</a></strong>.</p>
<script async type="text/javascript"
src="//cdn.carbonads.com/carbon.js?zoneid=1673&serve=CKYI5K3E&placement=pythonrequestsorg"
id="_carbonads_js"></script>
<h3>Useful Links</h3>
<ul>
<li><a href="https://requests.readthedocs.io/en/latest/user/quickstart/">Quickstart</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/user/advanced/">Advanced Usage</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/api/">API Reference</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/community/updates/#release-history">Release History</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/dev/contributing/">Contributors Guide</a></li>
<p></p>
<li><a href="https://requests.readthedocs.io/en/latest/community/recommended/">Recommended Packages and Extensions</a></li>
<p></p>
<li><a href="https://github.com/psf/requests">Requests @ GitHub</a></li>
<li><a href="https://pypi.org/project/requests/">Requests @ PyPI</a></li>
<li><a href="https://github.com/psf/requests/issues">Issue Tracker</a></li>
</ul>
<h3>Translations</h3>
<ul>
<li><a href="https://requests.readthedocs.io/">English</a></li>
<li><a href="https://fr.python-requests.org/">French</a></li>
<li><a href="https://de.python-requests.org/">German</a></li>
<li><a href="https://jp.python-requests.org/">Japanese</a></li>
<li><a href="https://cn.python-requests.org/">Chinese</a></li>
<li><a href="https://pt.python-requests.org/">Portuguese</a></li>
<li><a href="https://it.python-requests.org/">Italian</a></li>
<li><a href="https://es.python-requests.org/">Spanish</a></li>
</ul>
<div id="native-ribbon">
</div>

View File

@@ -1,71 +0,0 @@
<p>
<iframe src="https://ghbtns.com/github-btn.html?user=psf&repo=requests&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
</p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
<style>
.algolia-autocomplete {
width: 100%;
height: 1.5em
}
.algolia-autocomplete a {
border-bottom: none !important;
}
#doc_search {
width: 100%;
height: 100%;
}
</style>
<input id="doc_search" placeholder="Search the doc" autofocus />
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js" onload="docsearch({
apiKey: 'f177061e2354c50a97bfc635e827ffab',
indexName: 'python-requests',
inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown
})" async></script>
<p>
Requests is an elegant and simple HTTP library for Python, built for
human beings. You are currently looking at the documentation of the
development release.
</p>
<p>Sponsored by <strong><a href="https://www.govcert.lu">CERT Gouvernemental - GOVCERT.LU</a></strong>.</p>
<script async type="text/javascript"
src="//cdn.carbonads.com/carbon.js?zoneid=1673&serve=C6AILKT&placement=pythonrequestsorg"
id="_carbonads_js"></script>
<h3>Useful Links</h3>
<ul>
<li><a href="https://requests.readthedocs.io/en/latest/user/quickstart/">Quickstart</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/user/advanced/">Advanced Usage</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/api/">API Reference</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/community/updates/#release-history">Release History</a></li>
<li><a href="https://requests.readthedocs.io/en/latest/dev/contributing/">Contributors Guide</a></li>
<p></p>
<li><a href="https://requests.readthedocs.io/en/latest/community/recommended/">Recommended Packages and Extensions</a></li>
<p></p>
<li><a href="https://github.com/psf/requests">Requests @ GitHub</a></li>
<li><a href="https://pypi.org/project/requests/">Requests @ PyPI</a></li>
<li><a href="https://github.com/psf/requests/issues">Issue Tracker</a></li>
</ul>
<h3>Translations</h3>
<ul>
<li><a href="https://requests.readthedocs.io/">English</a></li>
<li><a href="https://fr.python-requests.org/">French</a></li>
<li><a href="https://de.python-requests.org/">German</a></li>
<li><a href="https://jp.python-requests.org/">Japanese</a></li>
<li><a href="https://cn.python-requests.org/">Chinese</a></li>
<li><a href="https://pt.python-requests.org/">Portuguese</a></li>
<li><a href="https://it.python-requests.org/">Italian</a></li>
<li><a href="https://es.python-requests.org/">Spanish</a></li>
</ul>

View File

@@ -31,11 +31,11 @@ Exceptions
.. autoexception:: requests.RequestException
.. autoexception:: requests.ConnectionError
.. autoexception:: requests.HTTPError
.. autoexception:: requests.URLRequired
.. autoexception:: requests.TooManyRedirects
.. autoexception:: requests.ConnectTimeout
.. autoexception:: requests.ReadTimeout
.. autoexception:: requests.Timeout
.. autoexception:: requests.JSONDecodeError
Request Sessions
@@ -55,6 +55,7 @@ Lower-Level Classes
.. autoclass:: Response
:inherited-members:
:exclude-members: is_permanent_redirect
Lower-Lower-Level Classes

View File

@@ -22,7 +22,7 @@ Custom User-Agents?
-------------------
Requests allows you to easily override User-Agent strings, along with
any other HTTP Header. See `documentation about headers <https://requests.readthedocs.io/en/latest/user/quickstart/#custom-headers>`_.
any other HTTP Header. See :ref:`documentation about headers <custom-headers>`.
@@ -45,7 +45,7 @@ Chris Adams gave an excellent summary on
for, oh, about 0.0001% of the awesomeness.
1. http://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
an annoying bug which affect many people, there was a fix available for
an annoying bug that affected many people, there was a fix available for
months, which worked great when I applied it in a fork and pounded a couple
TB of data through it, but it took over a year to make it into trunk and
even longer to make it onto PyPI where any other project which required "
@@ -55,17 +55,18 @@ Chris Adams gave an excellent summary on
Python 3 Support?
-----------------
Yes! Requests officially supports Python 2.7 & 3.6+ and PyPy.
Yes! Requests supports all `officially supported versions of Python <https://devguide.python.org/versions/>`_
and recent releases of PyPy.
Python 2 Support?
-----------------
Yes! We understand that we have a large user base with varying needs. Through
**at least** Requests 2.27.x, we will be providing continued support for Python
2.7. However, this support is likely to end some time in 2022.
No! As of Requests 2.28.0, Requests no longer supports Python 2.7. Users who
have been unable to migrate should pin to `requests<2.28`. Full information
can be found in `psf/requests#6023 <https://github.com/psf/requests/issues/6023>`_.
It is *highly* recommended users migrate to Python 3.7+ now since Python
2.7 is no longer receiving bug fixes or security updates as of January 1, 2020.
It is *highly* recommended users migrate to a supported Python 3.x version now since
Python 2.7 is no longer receiving bug fixes or security updates as of January 1, 2020.
What are "hostname doesn't match" errors?
-----------------------------------------
@@ -83,10 +84,7 @@ when servers are using `Virtual Hosting`_. When such servers are hosting
more than one SSL site they need to be able to return the appropriate
certificate based on the hostname the client is connecting to.
Python3 and Python 2.7.9+ include native support for SNI in their SSL modules.
For information on using SNI with Requests on Python < 2.7.9 refer to this
`Stack Overflow answer`_.
Python 3 already includes native support for SNI in their SSL modules.
.. _`Server-Name-Indication`: https://en.wikipedia.org/wiki/Server_Name_Indication
.. _`virtual hosting`: https://en.wikipedia.org/wiki/Virtual_hosting
.. _`Stack Overflow answer`: https://stackoverflow.com/questions/18578439/using-requests-with-tls-doesnt-give-sni-support/18579484#18579484

View File

@@ -1,22 +1,10 @@
Integrations
============
Python for iOS
--------------
Requests is built into the wonderful `Python for iOS <https://itunes.apple.com/us/app/python-2.7-for-ios/id485729872?mt=Python8>`_ runtime!
To give it a try, simply::
import requests
Articles & Talks
================
- `Python for the Web <https://www.gun.io/blog/python-for-the-web>`_ teaches how to use Python to interact with the web, using Requests.
- `Daniel Greenfeld's Review of Requests <https://pydanny.blogspot.com/2011/05/python-http-requests-for-humans.html>`_
- `My 'Python for Humans' talk <http://python-for-humans.heroku.com>`_ ( `audio <https://codeconf.s3.amazonaws.com/2011/pycodeconf/talks/PyCodeConf2011%20-%20Kenneth%20Reitz.m4a>`_ )
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.com/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.io/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
- `Blog post about Requests via Yum <https://arunsag.wordpress.com/2011/08/17/new-package-python-requests-http-for-humans/>`_
- `Russian blog post introducing Requests <https://habr.com/post/126262/>`_
- `Sending JSON in Requests <http://www.coglib.com/~icordasc/blog/2014/11/sending-json-in-requests.html>`_

View File

@@ -1,110 +1,5 @@
Vulnerability Disclosure
========================
If you think you have found a potential security vulnerability in requests,
please email `Nate <mailto:nate.prewitt@gmail.com>`_ and `Seth <mailto:@sethmichaellarson@gmail.com>`_ directly. **Do not file a public issue.**
Our PGP Key fingerprints are:
- 8722 7E29 AD9C FF5C FAC3 EA6A 44D3 FF97 B80D C864 (`@nateprewitt <https://keybase.io/nateprewitt>`_)
- EDD5 6765 A9D8 4653 CBC8 A134 51B0 6736 1740 F5FC (`@sethmlarson <https://keybase.io/sethmlarson>`_)
You can also contact us on `Keybase <https://keybase.io/>`_ with the
profiles above if desired.
If English is not your first language, please try to describe the problem and
its impact to the best of your ability. For greater detail, please use your
native language and we will try our best to translate it using online services.
Please also include the code you used to find the problem and the shortest
amount of code necessary to reproduce it.
Please do not disclose this to anyone else. We will retrieve a CVE identifier
if necessary and give you full credit under whatever name or alias you provide.
We will only request an identifier when we have a fix and can publish it in a
release.
We will respect your privacy and will only publicize your involvement if you
grant us permission.
Process
-------
This following information discusses the process the requests project follows
in response to vulnerability disclosures. If you are disclosing a
vulnerability, this section of the documentation lets you know how we will
respond to your disclosure.
Timeline
~~~~~~~~
When you report an issue, one of the project members will respond to you within
two days *at the outside*. In most cases responses will be faster, usually
within 12 hours. This initial response will at the very least confirm receipt
of the report.
If we were able to rapidly reproduce the issue, the initial response will also
contain confirmation of the issue. If we are not, we will often ask for more
information about the reproduction scenario.
Our goal is to have a fix for any vulnerability released within two weeks of
the initial disclosure. This may potentially involve shipping an interim
release that simply disables function while a more mature fix can be prepared,
but will in the vast majority of cases mean shipping a complete release as soon
as possible.
Throughout the fix process we will keep you up to speed with how the fix is
progressing. Once the fix is prepared, we will notify you that we believe we
have a fix. Often we will ask you to confirm the fix resolves the problem in
your environment, especially if we are not confident of our reproduction
scenario.
At this point, we will prepare for the release. We will obtain a CVE number
if one is required, providing you with full credit for the discovery. We will
also decide on a planned release date, and let you know when it is. This
release date will *always* be on a weekday.
At this point we will reach out to our major downstream packagers to notify
them of an impending security-related patch so they can make arrangements. In
addition, these packagers will be provided with the intended patch ahead of
time, to ensure that they are able to promptly release their downstream
packages. Currently the list of people we actively contact *ahead of a public
release* is:
- Python Maintenance Team, Red Hat (python-maint@redhat.com)
- Daniele Tricoli, Debian (@eriol)
We will notify these individuals at least a week ahead of our planned release
date to ensure that they have sufficient time to prepare. If you believe you
should be on this list, please let one of the maintainers know at one of the
email addresses at the top of this article.
On release day, we will push the patch to our public repository, along with an
updated changelog that describes the issue and credits you. We will then issue
a PyPI release containing the patch.
At this point, we will publicise the release. This will involve mails to
mailing lists, Tweets, and all other communication mechanisms available to the
core team.
We will also explicitly mention which commits contain the fix to make it easier
for other distributors and users to easily patch their own versions of requests
if upgrading is not an option.
Previous CVEs
-------------
- Fixed in 2.20.0
- `CVE 2018-18074 <https://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-18074>`_
- Fixed in 2.6.0
- `CVE 2015-2296 <https://cve.mitre.org/cgi-bin/cvename.cgi?name=2015-2296>`_,
reported by Matthew Daley of `BugFuzz <https://bugfuzz.com/>`_.
- Fixed in 2.3.0
- `CVE 2014-1829 <https://cve.mitre.org/cgi-bin/cvename.cgi?name=2014-1829>`_
- `CVE 2014-1830 <https://cve.mitre.org/cgi-bin/cvename.cgi?name=2014-1830>`_
The latest vulnerability disclosure information can be found on GitHub in our
`Security Policy <https://github.com/psf/requests/blob/main/.github/SECURITY.md>`_.

View File

@@ -54,11 +54,11 @@ source_suffix = ".rst"
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = "index"
root_doc = "index"
# General information about the project.
project = u"Requests"
copyright = u'MMXVIX. A <a href="https://kenreitz.org/projects">Kenneth Reitz</a> Project'
copyright = u"Kenneth Reitz and contributors"
author = u"Kenneth Reitz"
# The version info for the project you're documenting, acts as replacement for
@@ -126,7 +126,7 @@ html_theme = "alabaster"
# documentation.
html_theme_options = {
"show_powered_by": False,
"github_user": "requests",
"github_user": "psf",
"github_repo": "requests",
"github_banner": True,
"show_related": False,
@@ -172,14 +172,13 @@ html_use_smartypants = False
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
"index": ["sidebarintro.html", "sourcelink.html", "searchbox.html", "hacks.html"],
"index": ["sidebar.html", "sourcelink.html", "searchbox.html"],
"**": [
"sidebarlogo.html",
"sidebar.html",
"localtoc.html",
"relations.html",
"sourcelink.html",
"searchbox.html",
"hacks.html",
],
}
@@ -247,7 +246,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, "Requests.tex", u"Requests Documentation", u"Kenneth Reitz", "manual")
(root_doc, "Requests.tex", u"Requests Documentation", u"Kenneth Reitz", "manual")
]
# The name of an image file (relative to this directory) to place at the top of
@@ -275,7 +274,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "requests", u"Requests Documentation", [author], 1)]
man_pages = [(root_doc, "requests", u"Requests Documentation", [author], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -288,7 +287,7 @@ man_pages = [(master_doc, "requests", u"Requests Documentation", [author], 1)]
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
root_doc,
"Requests",
u"Requests Documentation",
author,

View File

@@ -22,19 +22,17 @@ The guide is split into sections based on the type of contribution you're
thinking of making, with a section that covers general guidelines for all
contributors.
Be Cordial
----------
Code of Conduct
---------------
**Be cordial or be on your way**. *—Kenneth Reitz*
The Python community is made up of members from around the globe with a diverse
set of skills, personalities, and experiences. It is through these differences
that our community experiences great successes and continued growth. When you're
working with members of the community, follow the
`Python Software Foundation Code of Conduct`_ to help steer your interactions
and keep Python a positive, successful, and growing community.
Requests has one very important rule governing all forms of contribution,
including reporting bugs or requesting features. This golden rule is
"`be cordial or be on your way`_".
**All contributions are welcome**, as long as
everyone involved is treated with respect.
.. _be cordial or be on your way: https://kenreitz.org/essays/2013/01/27/be-cordial-or-be-on-your-way
.. _Python Software Foundation Code of Conduct: https://policies.python.org/python.org/code-of-conduct/
.. _early-feedback:
@@ -93,6 +91,21 @@ event that you object to the code review feedback, you should make your case
clearly and calmly. If, after doing so, the feedback is judged to still apply,
you must either apply the feedback or withdraw your contribution.
Code Style
~~~~~~~~~~
Requests uses a collection of tools to ensure the code base has a consistent
style as it grows. We have these orchestrated using a tool called
`pre-commit`_. This can be installed locally and run over your changes prior
to opening a PR, and will also be run as part of the CI approval process
before a change is merged.
You can find the full list of formatting requirements specified in the
`.pre-commit-config.yaml`_ at the top level directory of Requests.
.. _pre-commit: https://pre-commit.com/
.. _.pre-commit-config.yaml: https://github.com/psf/requests/blob/main/.pre-commit-config.yaml
New Contributors
~~~~~~~~~~~~~~~~
@@ -103,62 +116,6 @@ asking for help.
Please also check the :ref:`early-feedback` section.
Kenneth Reitz's Code Style™
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Requests codebase uses the `PEP 8`_ code style.
In addition to the standards outlined in PEP 8, we have a few guidelines:
- Line-length can exceed 79 characters, to 100, when convenient.
- Line-length can exceed 100 characters, when doing otherwise would be *terribly* inconvenient.
- Always use single-quoted strings (e.g. ``'#flatearth'``), unless a single-quote occurs within the string.
Additionally, one of the styles that PEP8 recommends for `line continuations`_
completely lacks all sense of taste, and is not to be permitted within
the Requests codebase::
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
var_three, var_four)
No. Just don't. Please. This is much better::
foo = long_function_name(
var_one,
var_two,
var_three,
var_four,
)
Docstrings are to follow the following syntaxes::
def the_earth_is_flat():
"""NASA divided up the seas into thirty-three degrees."""
pass
::
def fibonacci_spiral_tool():
"""With my feet upon the ground I lose myself / between the sounds
and open wide to suck it in. / I feel it move across my skin. / I'm
reaching up and reaching out. / I'm reaching for the random or
whatever will bewilder me. / Whatever will bewilder me. / And
following our will and wind we may just go where no one's been. /
We'll ride the spiral to the end and may just go where no one's
been.
Spiral out. Keep going...
"""
pass
All functions, methods, and classes are to contain docstrings. Object data
model methods (e.g. ``__repr__``) are typically the exception to this rule.
Thanks for helping to make the world a better place!
.. _PEP 8: https://pep8.org/
.. _line continuations: https://www.python.org/dev/peps/pep-0008/#indentation
Documentation Contributions
---------------------------

View File

@@ -9,21 +9,25 @@ Requests: HTTP for Humans™
Release v\ |version|. (:ref:`Installation <install>`)
.. image:: https://pepy.tech/badge/requests/month
:target: https://pepy.tech/project/requests
:alt: Requests Downloads Per Month Badge
.. image:: https://img.shields.io/pypi/l/requests.svg
.. image:: https://img.shields.io/pypi/v/requests.svg?maxAge=86400
:target: https://pypi.org/project/requests/
:alt: License Badge
.. image:: https://img.shields.io/pypi/wheel/requests.svg
:target: https://pypi.org/project/requests/
:alt: Wheel Support Badge
:alt: PyPI Version Badge
.. image:: https://img.shields.io/pypi/pyversions/requests.svg
:target: https://pypi.org/project/requests/
:alt: Python Version Support Badge
:alt: Supported Versions Badge
.. image:: https://static.pepy.tech/badge/requests/month
:target: https://pepy.tech/project/requests
:alt: Downloads Per Month Badge
.. image:: https://img.shields.io/github/contributors/psf/requests.svg
:target: https://github.com/psf/requests/graphs/contributors
:alt: Contributors Badge
.. image:: https://readthedocs.org/projects/requests/badge/?version=latest
:target: https://requests.readthedocs.io
:alt: Documentation Badge
**Requests** is an elegant and simple HTTP library for Python, built for human beings.
@@ -72,7 +76,7 @@ Requests is ready for today's web.
- Chunked Requests
- ``.netrc`` Support
Requests officially supports Python 2.7 & 3.6+, and runs great on PyPy.
Requests officially supports Python 3.10+, and runs great on PyPy.
The User Guide

View File

@@ -1,3 +1,3 @@
# Pinning to avoid unexpected breakages.
# Used by RTD to generate docs.
Sphinx==4.2.0
Sphinx==7.2.6

View File

@@ -291,7 +291,7 @@ versions of Requests.
For the sake of security we recommend upgrading certifi frequently!
.. _HTTP persistent connection: https://en.wikipedia.org/wiki/HTTP_persistent_connection
.. _connection pooling: https://urllib3.readthedocs.io/en/latest/reference/index.html#module-urllib3.connectionpool
.. _connection pooling: https://urllib3.readthedocs.io/en/latest/reference/urllib3.connectionpool.html
.. _certifi: https://certifiio.readthedocs.io/
.. _Mozilla trust store: https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt
@@ -603,15 +603,25 @@ Alternatively you can configure it once for an entire
session.get('http://example.org')
When the proxies configuration is not overridden in python as shown above,
by default Requests relies on the proxy configuration defined by standard
environment variables ``http_proxy``, ``https_proxy``, ``no_proxy`` and
``curl_ca_bundle``. Uppercase variants of these variables are also supported.
.. warning:: Setting ``session.proxies`` may behave differently than expected.
Values provided will be overwritten by environmental proxies
(those returned by `urllib.request.getproxies <https://docs.python.org/3/library/urllib.request.html#urllib.request.getproxies>`_).
To ensure the use of proxies in the presence of environmental proxies,
explicitly specify the ``proxies`` argument on all individual requests as
initially explained above.
See `#2018 <https://github.com/psf/requests/issues/2018>`_ for details.
When the proxies configuration is not overridden per request as shown above,
Requests relies on the proxy configuration defined by standard
environment variables ``http_proxy``, ``https_proxy``, ``no_proxy``,
and ``all_proxy``. Uppercase variants of these variables are also supported.
You can therefore set them to configure Requests (only set the ones relevant
to your needs)::
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ export ALL_PROXY="socks5://10.10.1.10:3434"
$ python
>>> import requests
@@ -646,16 +656,18 @@ certificates trusted by Requests can be found with::
from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)
You override this default certificate bundle by setting the standard
``curl_ca_bundle`` environment variable to another file path::
You override this default certificate bundle by setting the ``REQUESTS_CA_BUNDLE``
(or ``CURL_CA_BUNDLE``) environment variable to another file path::
$ export curl_ca_bundle="/usr/local/myproxy_info/cacert.pem"
$ export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"
$ export https_proxy="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get('https://example.org')
.. _socks:
SOCKS
^^^^^
@@ -665,11 +677,9 @@ In addition to basic HTTP proxies, Requests also supports proxies using the
SOCKS protocol. This is an optional feature that requires that additional
third-party libraries be installed before use.
You can get the dependencies for this feature from ``pip``:
You can get the dependencies for this feature from ``pip``::
.. code-block:: bash
$ python -m pip install requests[socks]
$ python -m pip install 'requests[socks]'
Once you've installed those dependencies, using a SOCKS proxy is just as easy
as using a HTTP one::
@@ -707,10 +717,9 @@ If ``chardet`` is installed, ``requests`` uses it, however for python3
library is an LGPL-licenced dependency and some users of requests
cannot depend on mandatory LGPL-licensed dependencies.
When you install ``request`` without specifying ``[use_chardet_on_py3]]`` extra,
When you install ``requests`` without specifying ``[use_chardet_on_py3]`` extra,
and ``chardet`` is not already installed, ``requests`` uses ``charset-normalizer``
(MIT-licensed) to guess the encoding. For Python 2, ``requests`` uses only
``chardet`` and is a mandatory dependency there.
(MIT-licensed) to guess the encoding.
The only time Requests will not guess the encoding is if no explicit charset
is present in the HTTP headers **and** the ``Content-Type``
@@ -937,7 +946,7 @@ Link Headers
Many HTTP APIs feature Link headers. They make APIs more self describing and
discoverable.
GitHub uses these for `pagination <https://developer.github.com/v3/#pagination>`_
GitHub uses these for `pagination <https://docs.github.com/en/rest/guides/using-pagination-in-the-rest-api>`_
in their API, for example::
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
@@ -958,11 +967,9 @@ Requests will automatically parse these link headers and make them easily consum
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.
As of v1.0.0, Requests has moved to a modular internal design using Transport
Adapters. These objects 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
@@ -985,6 +992,10 @@ 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.
.. note:: The adapter will be chosen based on a longest prefix match. Be mindful
prefixes such as ``http://localhost`` will also match ``http://localhost.other.com``
or ``http://localhost@other.com``. It's recommended to terminate full hostnames with a ``/``.
Many of the details of implementing a Transport Adapter are beyond the scope of
this documentation, but take a look at the next example for a simple SSL use-
case. For more than that, you might look at subclassing the
@@ -1017,8 +1028,29 @@ library to use SSLv3::
num_pools=connections, maxsize=maxsize,
block=block, ssl_version=ssl.PROTOCOL_SSLv3)
.. _`described here`: https://kenreitz.org/essays/2012/06/14/the-future-of-python-http
Example: Automatic Retries
^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, Requests does not retry failed connections. However, it is possible
to implement automatic retries with a powerful array of features, including
backoff, within a Requests :class:`Session <requests.Session>` using the
`urllib3.util.Retry`_ class::
from urllib3.util import Retry
from requests import Session
from requests.adapters import HTTPAdapter
s = Session()
retries = Retry(
total=3,
backoff_factor=0.1,
status_forcelist=[502, 503, 504],
allowed_methods={'POST'},
)
s.mount('https://', HTTPAdapter(max_retries=retries))
.. _`urllib3`: https://github.com/urllib3/urllib3
.. _`urllib3.util.Retry`: https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry
.. _blocking-or-nonblocking:
@@ -1046,7 +1078,7 @@ Header Ordering
In unusual circumstances you may want to provide headers in an ordered manner. If you pass an ``OrderedDict`` to the ``headers`` keyword argument, that will provide the headers with an ordering. *However*, the ordering of the default headers used by Requests will be preferred, which means that if you override default headers in the ``headers`` keyword argument, they may appear out of order compared to other headers in that keyword argument.
If this is problematic, users should consider setting the default headers on a :class:`Session <requests.Session>` object, by setting :attr:`Session <requests.Session.headers>` to a custom ``OrderedDict``. That ordering will always be preferred.
If this is problematic, users should consider setting the default headers on a :class:`Session <requests.Session>` object, by setting :attr:`Session.headers <requests.Session.headers>` to a custom ``OrderedDict``. That ordering will always be preferred.
.. _timeouts:
@@ -1062,7 +1094,7 @@ The **connect** timeout is the number of seconds Requests will wait for your
client to establish a connection to a remote machine (corresponding to the
`connect()`_) call on the socket. It's a good practice to set connect timeouts
to slightly larger than a multiple of 3, which is the default `TCP packet
retransmission window <https://www.hjp.at/doc/rfc/rfc2988.txt>`_.
retransmission window <https://datatracker.ietf.org/doc/html/rfc2988>`_.
Once your client has connected to the server and sent the HTTP request, the
**read** timeout is the number of seconds the client will wait for the server
@@ -1087,4 +1119,18 @@ coffee.
r = requests.get('https://github.com', timeout=None)
.. note:: The connect timeout applies to each connection attempt to an IP address.
If multiple addresses exist for a domain name, the underlying ``urllib3`` will
try each address sequentially until one successfully connects.
This may lead to an effective total connection timeout *multiple* times longer
than the specified time, e.g. an unresponsive server having both IPv4 and IPv6
addresses will have its perceived timeout *doubled*, so take that into account
when setting the connection timeout.
.. note:: Neither the connect nor read timeouts are `wall clock`_. This means
that if you start a request, and look at the time, and then look at
the time when the request finishes or times out, the real-world time
may be greater than what you specified.
.. _`wall clock`: https://wiki.php.net/rfc/max_execution_wall_time
.. _`connect()`: https://linux.die.net/man/2/connect

View File

@@ -44,6 +44,16 @@ set with `headers=`.
If credentials for the hostname are found, the request is sent with HTTP Basic
Auth.
Requests will search for the netrc file at `~/.netrc`, `~/_netrc`, or at the path
specified by the `NETRC` environment variable. `~` denotes the user's home
directory, which is `$HOME` on Unix based systems and `%USERPROFILE%` on Windows.
Usage of netrc file can be disabled by setting `trust_env` to `False` in the
Requests session::
>>> s = requests.Session()
>>> s.trust_env = False
>>> s.get('https://httpbin.org/basic-auth/user/pass')
Digest Authentication
---------------------

View File

@@ -22,7 +22,7 @@ Requests is actively developed on GitHub, where the code is
You can either clone the public repository::
$ git clone git://github.com/psf/requests.git
$ git clone https://github.com/psf/requests.git
Or, download the `tarball <https://github.com/psf/requests/tarball/main>`_::

View File

@@ -129,7 +129,7 @@ You can also access the response body as bytes, for non-text requests::
The ``gzip`` and ``deflate`` transfer-encodings are automatically decoded for you.
The ``br`` transfer-encoding is automatically decoded for you if a Brotli library
like `brotli <https://pypi.org/project/brotli>`_ or `brotlicffi <https://pypi.org/project/brotli>`_ is installed.
like `brotli <https://pypi.org/project/brotli>`_ or `brotlicffi <https://pypi.org/project/brotlicffi>`_ is installed.
For example, to create an image from binary data returned by a request, you can
use the following code::
@@ -177,7 +177,7 @@ server, you can access ``r.raw``. If you want to do this, make sure you set
<urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
In general, however, you should use a pattern like this to save what is being
streamed to a file::
@@ -201,6 +201,8 @@ may better fit your use cases.
were returned, use ``Response.raw``.
.. _custom-headers:
Custom Headers
--------------
@@ -220,6 +222,7 @@ Note: Custom headers are given less precedence than more specific sources of inf
are specified in ``.netrc``, which in turn will be overridden by the ``auth=``
parameter. Requests will search for the netrc file at `~/.netrc`, `~/_netrc`,
or at the path specified by the `NETRC` environment variable.
Check details in :ref:`netrc authentication <authentication>`.
* Authorization headers will be removed if you get redirected off-host.
* Proxy-Authorization headers will be overridden by proxy credentials provided in the URL.
* Content-Length headers will be overridden when we can determine the length of the content.
@@ -237,7 +240,7 @@ dictionary of data will automatically be form-encoded when the request is made::
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> r = requests.post('https://httpbin.org/post', data=payload)
>>> print(r.text)
{
...
@@ -268,7 +271,9 @@ use the same key::
},
...
}
>>> r1.text == r2.text
>>> # httpbin may embed non-deterministic metadata,
>>> # so we only compare our submitted data here.
>>> r1.json()['form'] == r2.json()['form']
True
There are times that you may want to send data that is not form-encoded. If
@@ -566,6 +571,3 @@ All exceptions that Requests explicitly raises inherit from
-----------------------
Ready for more? Check out the :ref:`advanced <advanced>` section.
If you're on the job market, consider taking `this programming quiz <https://triplebyte.com/a/b1i2FB8/requests-docs-1>`_. A substantial donation will be made to this project, if you find a job through this platform.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

115
pyproject.toml Normal file
View File

@@ -0,0 +1,115 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "requests"
description = "Python HTTP for Humans."
readme = "README.md"
license = {text = "Apache-2.0"}
authors = [
{ name = "Kenneth Reitz", email = "me@kennethreitz.org" },
]
maintainers = [
{name = "Ian Stapleton Cordasco", email="graffatcolmingov@gmail.com"},
{name = "Nate Prewitt", email="nate.prewitt@gmail.com"}
]
requires-python = ">=3.10"
dependencies = [
"charset_normalizer>=2,<4",
"idna>=2.5,<4",
"urllib3>=1.26,<3",
"certifi>=2023.5.7"
]
dynamic = ["version"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Software Development :: Libraries"
]
[project.urls]
Documentation = "https://requests.readthedocs.io"
Source = "https://github.com/psf/requests"
[project.optional-dependencies]
security = []
socks = ["PySocks>=1.5.6, !=1.5.7"]
use_chardet_on_py3 = ["chardet>=3.0.2,<8"]
[dependency-groups]
test = [
"requests[socks]",
"pytest-httpbin==2.1.0",
"httpbin~=0.10.0",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"pytest>=3",
"trustme",
]
typecheck = [
"pyright",
"typing_extensions",
]
[tool.setuptools]
license-files = ["LICENSE", "NOTICE"]
[tool.setuptools.dynamic]
version = {attr = "requests.__version__.__version__"}
[tool.setuptools.packages.find]
where = ["src"]
[tool.ruff]
target-version = "py310"
src = ["src/requests", "tests"]
exclude = ["docs/", "ext/"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"T10", # flake8-debugger (replaces debug-statements hook)
]
ignore = ["E203", "E501", "UP031"]
per-file-ignores = {"src/requests/__init__.py" = ["E402", "F401"], "src/requests/compat.py" = ["E402", "F401"], "tests/compat.py" = ["F401"]}
[tool.ruff.lint.isort]
known-first-party = ["requests"]
[tool.ruff.format]
# Use black-compatible formatting
quote-style = "double"
indent-style = "space"
[tool.pytest.ini_options]
addopts = "--doctest-modules"
doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS"
minversion = "6.2"
testpaths = ["tests"]
[tool.pyright]
include = ["src/requests"]
typeCheckingMode = "strict"

View File

@@ -1,3 +0,0 @@
[pytest]
addopts = -p no:warnings --doctest-modules
doctest_optionflags= NORMALIZE_WHITESPACE ELLIPSIS

View File

@@ -1,152 +0,0 @@
# -*- coding: utf-8 -*-
# __
# /__) _ _ _ _ _/ _
# / ( (- (/ (/ (- _) / _)
# /
"""
Requests HTTP Library
~~~~~~~~~~~~~~~~~~~~~
Requests is an HTTP library, written in Python, for human beings.
Basic GET usage:
>>> import requests
>>> r = requests.get('https://www.python.org')
>>> r.status_code
200
>>> b'Python is a programming language' in r.content
True
... or POST:
>>> payload = dict(key1='value1', key2='value2')
>>> r = requests.post('https://httpbin.org/post', data=payload)
>>> print(r.text)
{
...
"form": {
"key1": "value1",
"key2": "value2"
},
...
}
The other HTTP methods are supported - see `requests.api`. Full documentation
is at <https://requests.readthedocs.io>.
:copyright: (c) 2017 by Kenneth Reitz.
:license: Apache 2.0, see LICENSE for more details.
"""
import urllib3
import warnings
from .exceptions import RequestsDependencyWarning
try:
from charset_normalizer import __version__ as charset_normalizer_version
except ImportError:
charset_normalizer_version = None
try:
from chardet import __version__ as chardet_version
except ImportError:
chardet_version = None
def check_compatibility(urllib3_version, chardet_version, charset_normalizer_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.26
assert major == 1
assert minor >= 21
assert minor <= 26
# Check charset_normalizer for compatibility.
if chardet_version:
major, minor, patch = chardet_version.split('.')[:3]
major, minor, patch = int(major), int(minor), int(patch)
# chardet_version >= 3.0.2, < 5.0.0
assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0)
elif charset_normalizer_version:
major, minor, patch = charset_normalizer_version.split('.')[:3]
major, minor, patch = int(major), int(minor), int(patch)
# charset_normalizer >= 2.0.0 < 3.0.0
assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0)
else:
raise Exception("You need either charset_normalizer or chardet installed")
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 ({}) may cause slowdown.'.format(cryptography_version)
warnings.warn(warning, RequestsDependencyWarning)
# Check imported dependencies for compatibility.
try:
check_compatibility(urllib3.__version__, chardet_version, charset_normalizer_version)
except (AssertionError, ValueError):
warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
"version!".format(urllib3.__version__, chardet_version, charset_normalizer_version),
RequestsDependencyWarning)
# Attempt to enable urllib3's fallback for SNI support
# if the standard library doesn't support SNI or the
# 'ssl' library isn't available.
try:
try:
import ssl
except ImportError:
ssl = None
if not getattr(ssl, "HAS_SNI", False):
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
# urllib3's DependencyWarnings should be silenced.
from urllib3.exceptions import DependencyWarning
warnings.simplefilter('ignore', DependencyWarning)
from .__version__ import __title__, __description__, __url__, __version__
from .__version__ import __build__, __author__, __author_email__, __license__
from .__version__ import __copyright__, __cake__
from . import utils
from . import packages
from .models import Request, Response, PreparedRequest
from .api import request, get, head, post, patch, put, delete, options
from .sessions import session, Session
from .status_codes import codes
from .exceptions import (
RequestException, Timeout, URLRequired,
TooManyRedirects, HTTPError, ConnectionError,
FileModeWarning, ConnectTimeout, ReadTimeout, JSONDecodeError
)
# Set default logging handler to avoid "No handler found" warnings.
import logging
from logging import NullHandler
logging.getLogger(__name__).addHandler(NullHandler())
# FileModeWarnings go off per the default.
warnings.simplefilter('default', FileModeWarning, append=True)

View File

@@ -1,14 +0,0 @@
# .-. .-. .-. . . .-. .-. .-. .-.
# |( |- |.| | | |- `-. | `-.
# ' ' `-' `-`.`-' `-' `-' ' `-'
__title__ = 'requests'
__description__ = 'Python HTTP for Humans.'
__url__ = 'https://requests.readthedocs.io'
__version__ = '2.27.1'
__build__ = 0x022701
__author__ = 'Kenneth Reitz'
__author_email__ = 'me@kennethreitz.org'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2022 Kenneth Reitz'
__cake__ = u'\u2728 \U0001f370 \u2728'

View File

@@ -1,305 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.auth
~~~~~~~~~~~~~
This module contains the authentication handlers for Requests.
"""
import os
import re
import time
import hashlib
import threading
import warnings
from base64 import b64encode
from .compat import urlparse, str, basestring
from .cookies import extract_cookies_to_jar
from ._internal_utils import to_native_string
from .utils import parse_dict_header
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
CONTENT_TYPE_MULTI_PART = 'multipart/form-data'
def _basic_auth_str(username, password):
"""Returns a Basic Auth string."""
# "I want us to put a big-ol' comment on top of it that
# says that this behaviour is dumb but we need to preserve
# it because people are relying on it."
# - Lukasa
#
# These are here solely to maintain backwards compatibility
# for things like ints. This will be removed in 3.0.0.
if not isinstance(username, basestring):
warnings.warn(
"Non-string usernames will no longer be supported in Requests "
"3.0.0. Please convert the object you've passed in ({!r}) to "
"a string or bytes object in the near future to avoid "
"problems.".format(username),
category=DeprecationWarning,
)
username = str(username)
if not isinstance(password, basestring):
warnings.warn(
"Non-string passwords will no longer be supported in Requests "
"3.0.0. Please convert the object you've passed in ({!r}) to "
"a string or bytes object in the near future to avoid "
"problems.".format(type(password)),
category=DeprecationWarning,
)
password = str(password)
# -- End Removal --
if isinstance(username, str):
username = username.encode('latin1')
if isinstance(password, str):
password = password.encode('latin1')
authstr = 'Basic ' + to_native_string(
b64encode(b':'.join((username, password))).strip()
)
return authstr
class AuthBase(object):
"""Base class that all auth implementations derive from"""
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):
self.username = username
self.password = password
def __eq__(self, other):
return all([
self.username == getattr(other, 'username', None),
self.password == getattr(other, 'password', None)
])
def __ne__(self, other):
return not self == other
def __call__(self, r):
r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
return r
class HTTPProxyAuth(HTTPBasicAuth):
"""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
class HTTPDigestAuth(AuthBase):
"""Attaches HTTP Digest Authentication to the given Request object."""
def __init__(self, username, password):
self.username = username
self.password = password
# Keep state in per-thread local storage
self._thread_local = threading.local()
def init_per_thread_state(self):
# Ensure state is initialized just once per-thread
if not hasattr(self._thread_local, 'init'):
self._thread_local.init = True
self._thread_local.last_nonce = ''
self._thread_local.nonce_count = 0
self._thread_local.chal = {}
self._thread_local.pos = None
self._thread_local.num_401_calls = None
def build_digest_header(self, method, url):
"""
:rtype: str
"""
realm = self._thread_local.chal['realm']
nonce = self._thread_local.chal['nonce']
qop = self._thread_local.chal.get('qop')
algorithm = self._thread_local.chal.get('algorithm')
opaque = self._thread_local.chal.get('opaque')
hash_utf8 = None
if algorithm is None:
_algorithm = 'MD5'
else:
_algorithm = algorithm.upper()
# lambdas assume digest modules are imported at the top level
if _algorithm == 'MD5' or _algorithm == 'MD5-SESS':
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':
def sha_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.sha1(x).hexdigest()
hash_utf8 = sha_utf8
elif _algorithm == 'SHA-256':
def sha256_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.sha256(x).hexdigest()
hash_utf8 = sha256_utf8
elif _algorithm == 'SHA-512':
def sha512_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.sha512(x).hexdigest()
hash_utf8 = sha512_utf8
KD = lambda s, d: hash_utf8("%s:%s" % (s, d))
if hash_utf8 is None:
return None
# XXX not implemented yet
entdig = None
p_parsed = urlparse(url)
#: path is request-uri defined in RFC 2616 which should not be empty
path = p_parsed.path or "/"
if p_parsed.query:
path += '?' + p_parsed.query
A1 = '%s:%s:%s' % (self.username, realm, self.password)
A2 = '%s:%s' % (method, path)
HA1 = hash_utf8(A1)
HA2 = hash_utf8(A2)
if nonce == self._thread_local.last_nonce:
self._thread_local.nonce_count += 1
else:
self._thread_local.nonce_count = 1
ncvalue = '%08x' % self._thread_local.nonce_count
s = str(self._thread_local.nonce_count).encode('utf-8')
s += nonce.encode('utf-8')
s += time.ctime().encode('utf-8')
s += os.urandom(8)
cnonce = (hashlib.sha1(s).hexdigest()[:16])
if _algorithm == 'MD5-SESS':
HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce))
if not qop:
respdig = KD(HA1, "%s:%s" % (nonce, HA2))
elif qop == 'auth' or 'auth' in qop.split(','):
noncebit = "%s:%s:%s:%s:%s" % (
nonce, ncvalue, cnonce, 'auth', HA2
)
respdig = KD(HA1, noncebit)
else:
# XXX handle auth-int.
return None
self._thread_local.last_nonce = nonce
# 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)
if opaque:
base += ', opaque="%s"' % opaque
if algorithm:
base += ', algorithm="%s"' % algorithm
if entdig:
base += ', digest="%s"' % entdig
if qop:
base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce)
return 'Digest %s' % (base)
def handle_redirect(self, r, **kwargs):
"""Reset num_401_calls counter on redirects."""
if r.is_redirect:
self._thread_local.num_401_calls = 1
def handle_401(self, r, **kwargs):
"""
Takes the given response and tries digest-auth, if needed.
:rtype: requests.Response
"""
# If response is not 4xx, do not auth
# See https://github.com/psf/requests/issues/3772
if not 400 <= r.status_code < 500:
self._thread_local.num_401_calls = 1
return r
if self._thread_local.pos is not None:
# Rewind the file position indicator of the body to where
# it was to resend the request.
r.request.body.seek(self._thread_local.pos)
s_auth = r.headers.get('www-authenticate', '')
if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2:
self._thread_local.num_401_calls += 1
pat = re.compile(r'digest ', flags=re.IGNORECASE)
self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1))
# Consume content and release the original connection
# to allow our new request to reuse the same one.
r.content
r.close()
prep = r.request.copy()
extract_cookies_to_jar(prep._cookies, r.request, r.raw)
prep.prepare_cookies(prep._cookies)
prep.headers['Authorization'] = self.build_digest_header(
prep.method, prep.url)
_r = r.connection.send(prep, **kwargs)
_r.history.append(r)
_r.request = prep
return _r
self._thread_local.num_401_calls = 1
return r
def __call__(self, r):
# Initialize per-thread state, if needed
self.init_per_thread_state()
# If we have a saved nonce, skip the 401
if self._thread_local.last_nonce:
r.headers['Authorization'] = self.build_digest_header(r.method, r.url)
try:
self._thread_local.pos = r.body.tell()
except AttributeError:
# In the case of HTTPDigestAuth being reused and the body of
# the previous request was a file-like object, pos has the
# file position of the previous body. Ensure it's set to
# None.
self._thread_local.pos = None
r.register_hook('response', self.handle_401)
r.register_hook('response', self.handle_redirect)
self._thread_local.num_401_calls = 1
return r
def __eq__(self, other):
return all([
self.username == getattr(other, 'username', None),
self.password == getattr(other, 'password', None)
])
def __ne__(self, other):
return not self == other

View File

@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.compat
~~~~~~~~~~~~~~~
This module handles import compatibility issues between Python 2 and
Python 3.
"""
try:
import chardet
except ImportError:
import charset_normalizer as chardet
import sys
# -------
# Pythons
# -------
# Syntax sugar.
_ver = sys.version_info
#: Python 2.x?
is_py2 = (_ver[0] == 2)
#: Python 3.x?
is_py3 = (_ver[0] == 3)
has_simplejson = False
try:
import simplejson as json
has_simplejson = True
except ImportError:
import json
# ---------
# Specifics
# ---------
if is_py2:
from urllib import (
quote, unquote, quote_plus, unquote_plus, urlencode, getproxies,
proxy_bypass, proxy_bypass_environment, getproxies_environment)
from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag
from urllib2 import parse_http_list
import cookielib
from Cookie import Morsel
from StringIO import StringIO
# Keep OrderedDict for backwards compatibility.
from collections import Callable, Mapping, MutableMapping, OrderedDict
builtin_str = str
bytes = str
str = unicode
basestring = basestring
numeric_types = (int, long, float)
integer_types = (int, long)
JSONDecodeError = ValueError
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, getproxies, proxy_bypass, proxy_bypass_environment, getproxies_environment
from http import cookiejar as cookielib
from http.cookies import Morsel
from io import StringIO
# Keep OrderedDict for backwards compatibility.
from collections import OrderedDict
from collections.abc import Callable, Mapping, MutableMapping
if has_simplejson:
from simplejson import JSONDecodeError
else:
from json import JSONDecodeError
builtin_str = str
str = str
bytes = bytes
basestring = (str, bytes)
numeric_types = (int, float)
integer_types = (int,)

View File

@@ -1,135 +0,0 @@
"""Module containing bug report helper(s)."""
from __future__ import print_function
import json
import platform
import sys
import ssl
import idna
import urllib3
from . import __version__ as requests_version
try:
import charset_normalizer
except ImportError:
charset_normalizer = None
try:
import chardet
except ImportError:
chardet = None
try:
from urllib3.contrib import pyopenssl
except ImportError:
pyopenssl = None
OpenSSL = None
cryptography = None
else:
import OpenSSL
import cryptography
def _implementation():
"""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
{'name': 'CPython', 'version': '2.7.5'}.
This function works best on CPython and PyPy: in particular, it probably
doesn't work for Jython or IronPython. Future investigation should be done
to work out the correct shape of the code for those platforms.
"""
implementation = platform.python_implementation()
if implementation == 'CPython':
implementation_version = platform.python_version()
elif implementation == 'PyPy':
implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major,
sys.pypy_version_info.minor,
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':
implementation_version = platform.python_version() # Complete Guess
elif implementation == 'IronPython':
implementation_version = platform.python_version() # Complete Guess
else:
implementation_version = 'Unknown'
return {'name': implementation, 'version': implementation_version}
def info():
"""Generate information for a bug report."""
try:
platform_info = {
'system': platform.system(),
'release': platform.release(),
}
except IOError:
platform_info = {
'system': 'Unknown',
'release': 'Unknown',
}
implementation_info = _implementation()
urllib3_info = {'version': urllib3.__version__}
charset_normalizer_info = {'version': None}
chardet_info = {'version': None}
if charset_normalizer:
charset_normalizer_info = {'version': charset_normalizer.__version__}
if chardet:
chardet_info = {'version': chardet.__version__}
pyopenssl_info = {
'version': None,
'openssl_version': '',
}
if OpenSSL:
pyopenssl_info = {
'version': OpenSSL.__version__,
'openssl_version': '%x' % OpenSSL.SSL.OPENSSL_VERSION_NUMBER,
}
cryptography_info = {
'version': getattr(cryptography, '__version__', ''),
}
idna_info = {
'version': getattr(idna, '__version__', ''),
}
system_ssl = ssl.OPENSSL_VERSION_NUMBER
system_ssl_info = {
'version': '%x' % system_ssl if system_ssl is not None else ''
}
return {
'platform': platform_info,
'implementation': implementation_info,
'system_ssl': system_ssl_info,
'using_pyopenssl': pyopenssl is not None,
'using_charset_normalizer': chardet is None,
'pyOpenSSL': pyopenssl_info,
'urllib3': urllib3_info,
'chardet': chardet_info,
'charset_normalizer': charset_normalizer_info,
'cryptography': cryptography_info,
'idna': idna_info,
'requests': {
'version': requests_version,
},
}
def main():
"""Pretty-print the bug information as JSON."""
print(json.dumps(info(), sort_keys=True, indent=2))
if __name__ == '__main__':
main()

View File

@@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.hooks
~~~~~~~~~~~~~~
This module provides the capabilities for the Requests hooks system.
Available hooks:
``response``:
The response generated from a Request.
"""
HOOKS = ['response']
def default_hooks():
return {event: [] for event in HOOKS}
# TODO: response is the only one
def dispatch_hook(key, hooks, hook_data, **kwargs):
"""Dispatches a hook dictionary on a given piece of data."""
hooks = hooks or {}
hooks = hooks.get(key)
if hooks:
if hasattr(hooks, '__call__'):
hooks = [hooks]
for hook in hooks:
_hook_data = hook(hook_data, **kwargs)
if _hook_data is not None:
hook_data = _hook_data
return hook_data

View File

@@ -1,26 +0,0 @@
import sys
try:
import chardet
except ImportError:
import charset_normalizer as chardet
import warnings
warnings.filterwarnings('ignore', 'Trying to detect', module='charset_normalizer')
# This code exists for backwards compatibility reasons.
# I don't like it either. Just look the other way. :)
for package in ('urllib3', 'idna'):
locals()[package] = __import__(package)
# This traversal is apparently necessary such that the identities are
# preserved (requests.packages.urllib3.* is urllib3.*)
for mod in list(sys.modules):
if mod == package or mod.startswith(package + '.'):
sys.modules['requests.packages.' + mod] = sys.modules[mod]
target = chardet.__name__
for mod in list(sys.modules):
if mod == target or mod.startswith(target + '.'):
sys.modules['requests.packages.' + target.replace(target, 'chardet')] = sys.modules[mod]
# Kinda cool, though, right?

View File

@@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
r"""
The ``codes`` object defines a mapping from common names for HTTP statuses
to their numerical codes, accessible either as attributes or as dictionary
items.
Example::
>>> import requests
>>> requests.codes['temporary_redirect']
307
>>> requests.codes.teapot
418
>>> requests.codes['\o/']
200
Some codes have multiple names, and both upper- and lower-case versions of
the names are allowed. For example, ``codes.ok``, ``codes.OK``, and
``codes.okay`` all correspond to the HTTP status code 200.
"""
from .structures import LookupDict
_codes = {
# Informational.
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', ''),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),
# Redirection.
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0
# Client Error.
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
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.
500: ('internal_server_error', 'server_error', '/o\\', ''),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication'),
}
codes = LookupDict(name='status_codes')
def _init():
for code, titles in _codes.items():
for title in titles:
setattr(codes, title, code)
if not title.startswith(('\\', '/')):
setattr(codes, title.upper(), code)
def doc(code):
names = ', '.join('``%s``' % n for n in _codes[code])
return '* %d: %s' % (code, names)
global __doc__
__doc__ = (__doc__ + '\n' +
'\n'.join(doc(code) for code in sorted(_codes))
if __doc__ is not None else None)
_init()

View File

@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.structures
~~~~~~~~~~~~~~~~~~~
Data structures that power Requests.
"""
from collections import OrderedDict
from .compat import Mapping, MutableMapping
class CaseInsensitiveDict(MutableMapping):
"""A case-insensitive ``dict``-like object.
Implements all methods and operations of
``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, regardless
of how the header name was originally stored.
If the constructor, ``.update``, or equality comparison
operations are given keys that have equal ``.lower()``s, the
behavior is undefined.
"""
def __init__(self, data=None, **kwargs):
self._store = OrderedDict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
def __getitem__(self, key):
return self._store[key.lower()][1]
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, Mapping):
other = CaseInsensitiveDict(other)
else:
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 str(dict(self.items()))
class LookupDict(dict):
"""Dictionary lookup object."""
def __init__(self, name=None):
self.name = name
super(LookupDict, self).__init__()
def __repr__(self):
return '<lookup \'%s\'>' % (self.name)
def __getitem__(self, key):
# We allow fall-through here, so values default to None
return self.__dict__.get(key, None)
def get(self, key, default=None):
return self.__dict__.get(key, default)

View File

@@ -1,8 +1,7 @@
pytest>=2.8.0,<=6.2.5
-e .[socks]
pytest>=2.8.0,<10
pytest-cov
pytest-httpbin==1.0.0
pytest-mock==2.0.0
httpbin==0.7.0
Flask>=1.0,<2.0
pytest-httpbin==2.1.0
httpbin~=0.10.0
trustme
wheel

View File

@@ -1,5 +0,0 @@
[bdist_wheel]
universal = 1
[metadata]
license_file = LICENSE

117
setup.py
View File

@@ -1,118 +1,9 @@
#!/usr/bin/env python
# Learn more: https://github.com/kennethreitz/setup.py
import os
import sys
from codecs import open
if sys.version_info < (3, 10): # noqa: UP036
sys.stderr.write("Requests requires Python 3.10 or later.\n")
sys.exit(1)
from setuptools import setup
from setuptools.command.test import test as TestCommand
here = os.path.abspath(os.path.dirname(__file__))
class PyTest(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass into py.test")]
def initialize_options(self):
TestCommand.initialize_options(self)
try:
from multiprocessing import cpu_count
self.pytest_args = ['-n', str(cpu_count()), '--boxed']
except (ImportError, NotImplementedError):
self.pytest_args = ['-n', '1', '--boxed']
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)
# 'setup.py publish' shortcut.
if sys.argv[-1] == 'publish':
os.system('python setup.py sdist bdist_wheel')
os.system('twine upload dist/*')
sys.exit()
packages = ['requests']
requires = [
'charset_normalizer~=2.0.0; python_version >= "3"',
'chardet>=3.0.2,<5; python_version < "3"',
'idna>=2.5,<3; python_version < "3"',
'idna>=2.5,<4; python_version >= "3"',
'urllib3>=1.21.1,<1.27',
'certifi>=2017.4.17'
]
test_requirements = [
'pytest-httpbin==0.0.7',
'pytest-cov',
'pytest-mock',
'pytest-xdist',
'PySocks>=1.5.6, !=1.5.7',
'pytest>=3'
]
about = {}
with open(os.path.join(here, 'requests', '__version__.py'), 'r', 'utf-8') as f:
exec(f.read(), about)
with open('README.md', 'r', 'utf-8') as f:
readme = f.read()
setup(
name=about['__title__'],
version=about['__version__'],
description=about['__description__'],
long_description=readme,
long_description_content_type='text/markdown',
author=about['__author__'],
author_email=about['__author_email__'],
url=about['__url__'],
packages=packages,
package_data={'': ['LICENSE', 'NOTICE']},
package_dir={'requests': 'requests'},
include_package_data=True,
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
install_requires=requires,
license=about['__license__'],
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries',
],
cmdclass={'test': PyTest},
tests_require=test_requirements,
extras_require={
'security': [],
'socks': ['PySocks>=1.5.6, !=1.5.7'],
'socks:sys_platform == "win32" and python_version == "2.7"': ['win_inet_pton'],
'use_chardet_on_py3': ['chardet>=3.0.2,<5']
},
project_urls={
'Documentation': 'https://requests.readthedocs.io',
'Source': 'https://github.com/psf/requests',
},
)
setup()

219
src/requests/__init__.py Normal file
View File

@@ -0,0 +1,219 @@
# __
# /__) _ _ _ _ _/ _
# / ( (- (/ (/ (- _) / _)
# /
"""
Requests HTTP Library
~~~~~~~~~~~~~~~~~~~~~
Requests is an HTTP library, written in Python, for human beings.
Basic GET usage:
>>> import requests
>>> r = requests.get('https://www.python.org')
>>> r.status_code
200
>>> b'Python is a programming language' in r.content
True
... or POST:
>>> payload = dict(key1='value1', key2='value2')
>>> r = requests.post('https://httpbin.org/post', data=payload)
>>> print(r.text)
{
...
"form": {
"key1": "value1",
"key2": "value2"
},
...
}
The other HTTP methods are supported - see `requests.api`. Full documentation
is at <https://requests.readthedocs.io>.
:copyright: (c) 2017 by Kenneth Reitz.
:license: Apache 2.0, see LICENSE for more details.
"""
from __future__ import annotations
import warnings
import urllib3
from .exceptions import RequestsDependencyWarning
try:
from charset_normalizer import __version__ as charset_normalizer_version
except ImportError:
charset_normalizer_version = None
try:
from chardet import __version__ as chardet_version # type: ignore[import-not-found]
except ImportError:
chardet_version = None
def check_compatibility(
urllib3_version: str,
chardet_version: str | None,
charset_normalizer_version: str | None,
) -> None:
urllib3_version_list = urllib3_version.split(".")[:3]
assert urllib3_version_list != ["dev"] # Verify urllib3 isn't installed from git.
# Sometimes, urllib3 only reports its version as 16.1.
if len(urllib3_version_list) == 2:
urllib3_version_list.append("0")
# Check urllib3 for compatibility.
major, minor, patch = urllib3_version_list # noqa: F811
major, minor, patch = int(major), int(minor), int(patch)
# urllib3 >= 1.21.1
assert major >= 1
if major == 1:
assert minor >= 21
# Check charset_normalizer for compatibility.
if chardet_version:
major, minor, patch = chardet_version.split(".")[:3]
major, minor, patch = int(major), int(minor), int(patch)
# chardet_version >= 3.0.2, < 8.0.0
assert (3, 0, 2) <= (major, minor, patch) < (8, 0, 0)
elif charset_normalizer_version:
major, minor, patch = charset_normalizer_version.split(".")[:3]
major, minor, patch = int(major), int(minor), int(patch)
# charset_normalizer >= 2.0.0 < 4.0.0
assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
else:
warnings.warn(
"Unable to find acceptable character detection dependency "
"(chardet or charset_normalizer).",
RequestsDependencyWarning,
)
def _check_cryptography(cryptography_version: str) -> None:
# cryptography < 1.3.4
try:
cryptography_version_list = list(map(int, cryptography_version.split(".")))
except ValueError:
return
if cryptography_version_list < [1, 3, 4]:
warning = f"Old version of cryptography ({cryptography_version_list}) may cause slowdown."
warnings.warn(warning, RequestsDependencyWarning)
# Check imported dependencies for compatibility.
try:
check_compatibility(
urllib3.__version__, # type: ignore[reportPrivateImportUsage]
chardet_version, # type: ignore[reportUnknownArgumentType]
charset_normalizer_version,
)
except (AssertionError, ValueError):
warnings.warn(
f"urllib3 ({urllib3.__version__}) or chardet " # type: ignore[reportPrivateImportUsage]
f"({chardet_version})/charset_normalizer ({charset_normalizer_version}) "
"doesn't match a supported version!",
RequestsDependencyWarning,
)
# Attempt to enable urllib3's fallback for SNI support
# if the standard library doesn't support SNI or the
# 'ssl' library isn't available.
try:
try:
import ssl
except ImportError:
ssl = None
if not getattr(ssl, "HAS_SNI", False):
from urllib3.contrib import pyopenssl
pyopenssl.inject_into_urllib3()
# Check cryptography version
from cryptography import ( # type: ignore[reportMissingImports]
__version__ as cryptography_version, # type: ignore[reportUnknownVariableType]
)
_check_cryptography(cryptography_version) # type: ignore[reportUnknownArgumentType]
except ImportError:
pass
# urllib3's DependencyWarnings should be silenced.
from urllib3.exceptions import DependencyWarning
warnings.simplefilter("ignore", DependencyWarning)
# Set default logging handler to avoid "No handler found" warnings.
import logging
from logging import NullHandler
from . import packages, utils
from .__version__ import (
__author__,
__author_email__,
__build__,
__cake__,
__copyright__,
__description__,
__license__,
__title__,
__url__,
__version__,
)
from .api import delete, get, head, options, patch, post, put, request
from .exceptions import (
ConnectionError,
ConnectTimeout,
FileModeWarning,
HTTPError,
JSONDecodeError,
ReadTimeout,
RequestException,
Timeout,
TooManyRedirects,
URLRequired,
)
from .models import PreparedRequest, Request, Response
from .sessions import Session, session
from .status_codes import codes
__all__ = (
"ConnectionError",
"ConnectTimeout",
"HTTPError",
"JSONDecodeError",
"PreparedRequest",
"ReadTimeout",
"Request",
"RequestException",
"Response",
"Session",
"Timeout",
"TooManyRedirects",
"URLRequired",
"codes",
"delete",
"get",
"head",
"options",
"packages",
"patch",
"post",
"put",
"request",
"session",
"utils",
)
logging.getLogger(__name__).addHandler(NullHandler())
# FileModeWarnings go off per the default.
warnings.simplefilter("default", FileModeWarning, append=True)

View File

@@ -0,0 +1,14 @@
# .-. .-. .-. . . .-. .-. .-. .-.
# |( |- |.| | | |- `-. | `-.
# ' ' `-' `-`.`-' `-' `-' ' `-'
__title__ = "requests"
__description__ = "Python HTTP for Humans."
__url__ = "https://requests.readthedocs.io"
__version__ = "2.34.0.dev1"
__build__ = 0x023400
__author__ = "Kenneth Reitz"
__author_email__ = "me@kennethreitz.org"
__license__ = "Apache-2.0"
__copyright__ = "Copyright Kenneth Reitz"
__cake__ = "\u2728 \U0001f370 \u2728"

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
requests._internal_utils
~~~~~~~~~~~~~~
@@ -8,10 +6,24 @@ Provides utility functions that are consumed internally by Requests
which depend on extremely few external helpers (such as compat)
"""
from .compat import is_py2, builtin_str, str
import re
from .compat import builtin_str
_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*\Z")
_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*\Z")
_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*\Z|^\Z")
_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*\Z|^\Z")
_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR)
_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE)
HEADER_VALIDATORS = {
bytes: _HEADER_VALIDATORS_BYTE,
str: _HEADER_VALIDATORS_STR,
}
def to_native_string(string, encoding='ascii'):
def to_native_string(string: str | bytes, encoding: str = "ascii") -> str:
"""Given a string object, regardless of type, returns a representation of
that string in the native string type, encoding and decoding where
necessary. This assumes ASCII unless told otherwise.
@@ -19,15 +31,12 @@ def to_native_string(string, encoding='ascii'):
if isinstance(string, builtin_str):
out = string
else:
if is_py2:
out = string.encode(encoding)
else:
out = string.decode(encoding)
out = string.decode(encoding)
return out
def unicode_is_ascii(u_string):
def unicode_is_ascii(u_string: str) -> bool:
"""Determine if unicode string only contains ASCII characters.
:param str u_string: unicode string to check. Must be unicode
@@ -36,7 +45,7 @@ def unicode_is_ascii(u_string):
"""
assert isinstance(u_string, str)
try:
u_string.encode('ascii')
u_string.encode("ascii")
return True
except UnicodeEncodeError:
return False

176
src/requests/_types.py Normal file
View File

@@ -0,0 +1,176 @@
"""
requests._types
~~~~~~~~~~~~~~~
This module contains type aliases used internally by the Requests library.
These types are not part of the public API and must not be relied upon
by external code.
"""
from __future__ import annotations
from collections.abc import Callable, Iterable, Mapping, MutableMapping
from typing import (
TYPE_CHECKING,
Any,
Protocol,
TypeAlias,
TypeVar,
runtime_checkable,
)
_T_co = TypeVar("_T_co", covariant=True)
@runtime_checkable
class SupportsRead(Protocol[_T_co]):
def read(self, length: int = ..., /) -> _T_co: ...
@runtime_checkable
class SupportsItems(Protocol):
def items(self) -> Iterable[tuple[Any, Any]]: ...
# These are needed at runtime for default_hooks() return type
HookType: TypeAlias = Callable[["Response"], Any]
HooksInputType: TypeAlias = Mapping[str, Iterable[HookType] | HookType]
def is_prepared(request: PreparedRequest) -> TypeIs[_ValidatedRequest]:
"""Verify a PreparedRequest has been fully prepared."""
if TYPE_CHECKING:
return request.url is not None and request.method is not None
# noop at runtime to avoid AssertionError
return True
if TYPE_CHECKING:
from http.cookiejar import CookieJar
from typing import TypeAlias, TypedDict
from typing_extensions import (
Buffer, # TODO: move to collections.abc when Python >= 3.12
TypeIs, # TODO: move to typing when Python >= 3.13
)
from .auth import AuthBase
from .cookies import RequestsCookieJar
from .models import PreparedRequest, Response
from .structures import CaseInsensitiveDict
class _ValidatedRequest(PreparedRequest):
"""Subtype asserting a PreparedRequest has been fully prepared before calling.
The override suppression is required because mutable attribute types are
invariant (Liskov), but we only narrow after preparation is complete. This
is the explicit contract for Requests but Python's typing doesn't have a
better way to represent the requirement.
"""
url: str # type: ignore[reportIncompatibleVariableOverride]
method: str # type: ignore[reportIncompatibleVariableOverride]
# Type aliases for core API concepts (ordered by request() signature)
UriType: TypeAlias = str | bytes
_ParamsMappingKeyType: TypeAlias = str | bytes | int | float
_ParamsMappingValueType: TypeAlias = (
str | bytes | int | float | Iterable[str | bytes | int | float] | None
)
ParamsType: TypeAlias = (
Mapping[_ParamsMappingKeyType, _ParamsMappingValueType]
| tuple[tuple[_ParamsMappingKeyType, _ParamsMappingValueType], ...]
| Iterable[tuple[_ParamsMappingKeyType, _ParamsMappingValueType]]
| str
| bytes
| None
)
KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | Mapping[Any, Any]
RawDataType: TypeAlias = KVDataType | str | bytes
StreamDataType: TypeAlias = SupportsRead[str | bytes]
EncodableDataType: TypeAlias = RawDataType | StreamDataType
DataType: TypeAlias = (
KVDataType
| Iterable[bytes | str]
| str
| bytes
| Buffer
| SupportsRead[str | bytes]
| None
)
BodyType: TypeAlias = (
bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None
)
HeadersType: TypeAlias = CaseInsensitiveDict[str] | Mapping[str, str | bytes]
HeadersUpdateType: TypeAlias = Mapping[str, str | bytes | None]
CookiesType: TypeAlias = RequestsCookieJar | CookieJar | None
# Building blocks for FilesType
_FileName: TypeAlias = str | None
_FileContent: TypeAlias = SupportsRead[str | bytes] | str | bytes
_FileSpecBasic: TypeAlias = tuple[_FileName, _FileContent]
_FileSpecWithContentType: TypeAlias = tuple[_FileName, _FileContent, str]
_FileSpecWithHeaders: TypeAlias = tuple[
_FileName, _FileContent, str, CaseInsensitiveDict[str] | Mapping[str, str]
]
_FileSpec: TypeAlias = (
_FileContent | _FileSpecBasic | _FileSpecWithContentType | _FileSpecWithHeaders
)
FilesType: TypeAlias = (
Mapping[str, _FileSpec] | Iterable[tuple[str, _FileSpec]] | None
)
AuthType: TypeAlias = (
tuple[str, str] | AuthBase | Callable[[PreparedRequest], PreparedRequest] | None
)
TimeoutType: TypeAlias = float | tuple[float | None, float | None] | None
ProxiesType: TypeAlias = MutableMapping[str, str]
HooksType: TypeAlias = dict[str, list[HookType]] | None
VerifyType: TypeAlias = bool | str
CertType: TypeAlias = str | tuple[str, str] | None
JsonType: TypeAlias = (
None | bool | int | float | str | list["JsonType"] | dict[str, "JsonType"]
)
# TypedDicts for Unpack kwargs (PEP 692)
class BaseRequestKwargs(TypedDict, total=False):
headers: Mapping[str, str | bytes] | None
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None
files: FilesType
auth: AuthType
timeout: TimeoutType
allow_redirects: bool
proxies: dict[str, str] | None
hooks: HooksType
stream: bool | None
verify: VerifyType | None
cert: CertType
class RequestKwargs(BaseRequestKwargs, total=False):
"""kwargs for request(), options(), head(), delete()."""
params: ParamsType
data: DataType
json: JsonType
class GetKwargs(BaseRequestKwargs, total=False):
data: DataType
json: JsonType
class PostKwargs(BaseRequestKwargs, total=False):
params: ParamsType
class DataKwargs(BaseRequestKwargs, total=False):
"""kwargs for put(), patch()."""
params: ParamsType
json: JsonType

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
requests.adapters
~~~~~~~~~~~~~~~~~
@@ -8,59 +6,134 @@ This module contains the transport adapters that Requests uses to define
and maintain connections.
"""
import os.path
import socket
from __future__ import annotations
from urllib3.poolmanager import PoolManager, proxy_from_url
from urllib3.response import HTTPResponse
from urllib3.util import parse_url
from urllib3.util import Timeout as TimeoutSauce
from urllib3.util.retry import Retry
from urllib3.exceptions import ClosedPoolError
from urllib3.exceptions import ConnectTimeoutError
import os.path
import socket # noqa: F401 # type: ignore[reportUnusedImport]
import typing
import warnings
from typing import Any
from urllib3.exceptions import (
ClosedPoolError,
ConnectTimeoutError,
LocationValueError,
MaxRetryError,
NewConnectionError,
ProtocolError,
ReadTimeoutError,
ResponseError,
)
from urllib3.exceptions import HTTPError as _HTTPError
from urllib3.exceptions import InvalidHeader as _InvalidHeader
from urllib3.exceptions import MaxRetryError
from urllib3.exceptions import NewConnectionError
from urllib3.exceptions import ProxyError as _ProxyError
from urllib3.exceptions import ProtocolError
from urllib3.exceptions import ReadTimeoutError
from urllib3.exceptions import SSLError as _SSLError
from urllib3.exceptions import ResponseError
from urllib3.exceptions import LocationValueError
from urllib3.poolmanager import PoolManager, proxy_from_url
from urllib3.util import Timeout as TimeoutSauce
from urllib3.util import parse_url
from urllib3.util.retry import Retry
from .models import Response
from .compat import urlparse, basestring
from .utils import (DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths,
get_encoding_from_headers, prepend_scheme_if_needed,
get_auth_from_url, urldefragauth, select_proxy)
from .structures import CaseInsensitiveDict
from .auth import _basic_auth_str # type: ignore[reportPrivateUsage]
from .compat import basestring, urlparse
from .cookies import extract_cookies_to_jar
from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError,
ProxyError, RetryError, InvalidSchema, InvalidProxyURL,
InvalidURL, InvalidHeader)
from .auth import _basic_auth_str
from .exceptions import (
ConnectionError,
ConnectTimeout,
InvalidHeader,
InvalidProxyURL,
InvalidSchema,
InvalidURL,
ProxyError,
ReadTimeout,
RetryError,
SSLError,
)
from .models import Response
from .structures import CaseInsensitiveDict
from .utils import (
DEFAULT_CA_BUNDLE_PATH,
get_auth_from_url,
get_encoding_from_headers,
prepend_scheme_if_needed,
select_proxy,
urldefragauth,
)
try:
from urllib3.contrib.socks import SOCKSProxyManager
from urllib3.contrib.socks import SOCKSProxyManager # type: ignore[assignment]
except ImportError:
def SOCKSProxyManager(*args, **kwargs):
def SOCKSProxyManager(*args: Any, **kwargs: Any) -> None:
raise InvalidSchema("Missing dependencies for SOCKS support.")
if typing.TYPE_CHECKING:
from urllib3.connectionpool import HTTPConnectionPool
from urllib3.poolmanager import PoolManager as _PoolManager
from . import _types as _t
from .models import PreparedRequest
from ._types import is_prepared as _is_prepared
DEFAULT_POOLBLOCK = False
DEFAULT_POOLSIZE = 10
DEFAULT_RETRIES = 0
DEFAULT_POOL_TIMEOUT = None
class BaseAdapter(object):
def _urllib3_request_context(
request: PreparedRequest,
verify: bool | str | None,
client_cert: tuple[str, str] | str | None,
poolmanager: PoolManager,
) -> tuple[dict[str, Any], dict[str, Any]]:
host_params: dict[str, Any] = {}
pool_kwargs: dict[str, Any] = {}
parsed_request_url = urlparse(request.url)
scheme = parsed_request_url.scheme.lower()
port = parsed_request_url.port
cert_reqs = "CERT_REQUIRED"
if verify is False:
cert_reqs = "CERT_NONE"
elif isinstance(verify, str):
if not os.path.isdir(verify):
pool_kwargs["ca_certs"] = verify
else:
pool_kwargs["ca_cert_dir"] = verify
pool_kwargs["cert_reqs"] = cert_reqs
if client_cert is not None:
if isinstance(client_cert, tuple) and len(client_cert) == 2:
pool_kwargs["cert_file"] = client_cert[0]
pool_kwargs["key_file"] = client_cert[1]
else:
# According to our docs, we allow users to specify just the client
# cert path
pool_kwargs["cert_file"] = client_cert
host_params = {
"scheme": scheme,
"host": parsed_request_url.hostname,
"port": port,
}
return host_params, pool_kwargs
class BaseAdapter:
"""The Base Transport Adapter"""
def __init__(self):
super(BaseAdapter, self).__init__()
def __init__(self) -> None:
super().__init__()
def send(self, request, stream=False, timeout=None, verify=True,
cert=None, proxies=None):
def send(
self,
request: PreparedRequest,
stream: bool = False,
timeout: _t.TimeoutType = None,
verify: _t.VerifyType = True,
cert: _t.CertType = None,
proxies: dict[str, str] | None = None,
) -> Response:
"""Sends PreparedRequest object. Returns Response object.
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
@@ -77,7 +150,7 @@ class BaseAdapter(object):
"""
raise NotImplementedError
def close(self):
def close(self) -> None:
"""Cleans up adapter specific items."""
raise NotImplementedError
@@ -108,31 +181,42 @@ class HTTPAdapter(BaseAdapter):
>>> a = requests.adapters.HTTPAdapter(max_retries=3)
>>> s.mount('http://', a)
"""
__attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize',
'_pool_block']
def __init__(self, pool_connections=DEFAULT_POOLSIZE,
pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
pool_block=DEFAULT_POOLBLOCK):
__attrs__: list[str] = [
"max_retries",
"config",
"_pool_connections",
"_pool_maxsize",
"_pool_block",
]
def __init__(
self,
pool_connections: int = DEFAULT_POOLSIZE,
pool_maxsize: int = DEFAULT_POOLSIZE,
max_retries: int | Retry = DEFAULT_RETRIES,
pool_block: bool = DEFAULT_POOLBLOCK,
) -> None:
if max_retries == DEFAULT_RETRIES:
self.max_retries = Retry(0, read=False)
self.max_retries: Retry = Retry(0, read=False)
else:
self.max_retries = Retry.from_int(max_retries)
self.config = {}
self.proxy_manager = {}
self.config: dict[str, Any] = {}
self.proxy_manager: dict[str, Any] = {}
super(HTTPAdapter, self).__init__()
super().__init__()
self._pool_connections = pool_connections
self._pool_maxsize = pool_maxsize
self._pool_block = pool_block
self._pool_connections: int = pool_connections
self._pool_maxsize: int = pool_maxsize
self._pool_block: bool = pool_block
self.poolmanager: _PoolManager
self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
def __getstate__(self):
def __getstate__(self) -> dict[str, Any]:
return {attr: getattr(self, attr, None) for attr in self.__attrs__}
def __setstate__(self, state):
def __setstate__(self, state: dict[str, Any]) -> None:
# Can't handle by adding 'proxy_manager' to self.__attrs__ because
# self.poolmanager uses a lambda function, which isn't pickleable.
self.proxy_manager = {}
@@ -141,10 +225,17 @@ class HTTPAdapter(BaseAdapter):
for attr, value in state.items():
setattr(self, attr, value)
self.init_poolmanager(self._pool_connections, self._pool_maxsize,
block=self._pool_block)
self.init_poolmanager(
self._pool_connections, self._pool_maxsize, block=self._pool_block
)
def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
def init_poolmanager(
self,
connections: int,
maxsize: int,
block: bool = DEFAULT_POOLBLOCK,
**pool_kwargs: Any,
) -> None:
"""Initializes a urllib3 PoolManager.
This method should not be called from user code, and is only
@@ -161,10 +252,14 @@ class HTTPAdapter(BaseAdapter):
self._pool_maxsize = maxsize
self._pool_block = block
self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize,
block=block, strict=True, **pool_kwargs)
self.poolmanager = PoolManager(
num_pools=connections,
maxsize=maxsize,
block=block,
**pool_kwargs,
)
def proxy_manager_for(self, proxy, **proxy_kwargs):
def proxy_manager_for(self, proxy: str, **proxy_kwargs: Any) -> Any:
"""Return urllib3 ProxyManager for the given proxy.
This method should not be called from user code, and is only
@@ -178,7 +273,7 @@ class HTTPAdapter(BaseAdapter):
"""
if proxy in self.proxy_manager:
manager = self.proxy_manager[proxy]
elif proxy.lower().startswith('socks'):
elif proxy.lower().startswith("socks"):
username, password = get_auth_from_url(proxy)
manager = self.proxy_manager[proxy] = SOCKSProxyManager(
proxy,
@@ -187,7 +282,7 @@ class HTTPAdapter(BaseAdapter):
num_pools=self._pool_connections,
maxsize=self._pool_maxsize,
block=self._pool_block,
**proxy_kwargs
**proxy_kwargs,
)
else:
proxy_headers = self.proxy_headers(proxy)
@@ -197,11 +292,14 @@ class HTTPAdapter(BaseAdapter):
num_pools=self._pool_connections,
maxsize=self._pool_maxsize,
block=self._pool_block,
**proxy_kwargs)
**proxy_kwargs,
)
return manager
def cert_verify(self, conn, url, verify, cert):
def cert_verify(
self, conn: Any, url: str, verify: _t.VerifyType, cert: _t.CertType
) -> None:
"""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>`.
@@ -213,8 +311,7 @@ class HTTPAdapter(BaseAdapter):
to a CA bundle to use
:param cert: The SSL certificate to verify.
"""
if url.lower().startswith('https') and verify:
if url.lower().startswith("https") and verify:
cert_loc = None
# Allow self-specified cert location.
@@ -222,20 +319,22 @@ class HTTPAdapter(BaseAdapter):
cert_loc = verify
if not cert_loc:
cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
cert_loc = DEFAULT_CA_BUNDLE_PATH
if not cert_loc or not os.path.exists(cert_loc):
raise IOError("Could not find a suitable TLS CA certificate bundle, "
"invalid path: {}".format(cert_loc))
raise OSError(
f"Could not find a suitable TLS CA certificate bundle, "
f"invalid path: {cert_loc}"
)
conn.cert_reqs = 'CERT_REQUIRED'
conn.cert_reqs = "CERT_REQUIRED"
if not os.path.isdir(cert_loc):
conn.ca_certs = cert_loc
else:
conn.ca_cert_dir = cert_loc
else:
conn.cert_reqs = 'CERT_NONE'
conn.cert_reqs = "CERT_NONE"
conn.ca_certs = None
conn.ca_cert_dir = None
@@ -247,13 +346,16 @@ class HTTPAdapter(BaseAdapter):
conn.cert_file = cert
conn.key_file = None
if conn.cert_file and not os.path.exists(conn.cert_file):
raise IOError("Could not find the TLS certificate file, "
"invalid path: {}".format(conn.cert_file))
raise OSError(
f"Could not find the TLS certificate file, "
f"invalid path: {conn.cert_file}"
)
if conn.key_file and not os.path.exists(conn.key_file):
raise IOError("Could not find the TLS key file, "
"invalid path: {}".format(conn.key_file))
raise OSError(
f"Could not find the TLS key file, invalid path: {conn.key_file}"
)
def build_response(self, req, resp):
def build_response(self, req: PreparedRequest, resp: Any) -> Response:
"""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
@@ -263,13 +365,14 @@ class HTTPAdapter(BaseAdapter):
:param resp: The urllib3 response object.
:rtype: requests.Response
"""
assert _is_prepared(req)
response = Response()
# Fallback to None if there's no status_code, for whatever reason.
response.status_code = getattr(resp, 'status', None)
response.status_code = getattr(resp, "status", None) # type: ignore[assignment]
# Make headers case-insensitive.
response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {}))
response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
# Set encoding.
response.encoding = get_encoding_from_headers(response.headers)
@@ -277,7 +380,7 @@ class HTTPAdapter(BaseAdapter):
response.reason = response.raw.reason
if isinstance(req.url, bytes):
response.url = req.url.decode('utf-8')
response.url = req.url.decode("utf-8")
else:
response.url = req.url
@@ -290,23 +393,148 @@ class HTTPAdapter(BaseAdapter):
return response
def get_connection(self, url, proxies=None):
"""Returns a urllib3 connection for the given URL. This should not be
def build_connection_pool_key_attributes(
self, request: PreparedRequest, verify: _t.VerifyType, cert: _t.CertType = None
) -> tuple[dict[str, Any], dict[str, Any]]:
"""Build the PoolKey attributes used by urllib3 to return a connection.
This looks at the PreparedRequest, the user-specified verify value,
and the value of the cert parameter to determine what PoolKey values
to use to select a connection from a given urllib3 Connection Pool.
The SSL related pool key arguments are not consistently set. As of
this writing, use the following to determine what keys may be in that
dictionary:
* If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
default Requests SSL Context
* If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
``"cert_reqs"`` will be set
* If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
``"ca_certs"`` will be set if the string is not a directory recognized
by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
set.
* If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
``"cert"`` is a tuple with a second item, ``"key_file"`` will also
be present
To override these settings, one may subclass this class, call this
method and use the above logic to change parameters as desired. For
example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
must both set ``"ssl_context"`` and based on what else they require,
alter the other keys to ensure the desired behaviour.
:param request:
The PreparedRequest being sent over the connection.
:type request:
:class:`~requests.models.PreparedRequest`
:param verify:
Either a boolean, in which case it controls whether
we verify the server's TLS certificate, or a string, in which case it
must be a path to a CA bundle to use.
:param cert:
(optional) Any user-provided SSL certificate for client
authentication (a.k.a., mTLS). This may be a string (i.e., just
the path to a file which holds both certificate and key) or a
tuple of length 2 with the certificate file path and key file
path.
:returns:
A tuple of two dictionaries. The first is the "host parameters"
portion of the Pool Key including scheme, hostname, and port. The
second is a dictionary of SSLContext related parameters.
"""
return _urllib3_request_context(request, verify, cert, self.poolmanager)
def get_connection_with_tls_context(
self,
request: PreparedRequest,
verify: _t.VerifyType,
proxies: dict[str, str] | None = None,
cert: _t.CertType = None,
) -> HTTPConnectionPool:
"""Returns a urllib3 connection for the given request and TLS settings.
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>` object to be sent
over the connection.
:param verify:
Either a boolean, in which case it controls whether we verify the
server's TLS certificate, or a string, in which case it must be a
path to a CA bundle to use.
:param proxies:
(optional) The proxies dictionary to apply to the request.
:param cert:
(optional) Any user-provided SSL certificate to be used for client
authentication (a.k.a., mTLS).
:rtype:
urllib3.HTTPConnectionPool
"""
assert _is_prepared(request)
proxy = select_proxy(request.url, proxies)
try:
host_params, pool_kwargs = self.build_connection_pool_key_attributes(
request,
verify,
cert,
)
except ValueError as e:
raise InvalidURL(e, request=request)
if proxy:
proxy = prepend_scheme_if_needed(proxy, "http")
proxy_url = parse_url(proxy)
if not proxy_url.host:
raise InvalidProxyURL(
"Please check proxy URL. It is malformed "
"and could be missing the host."
)
proxy_manager = self.proxy_manager_for(proxy)
conn = proxy_manager.connection_from_host(
**host_params, pool_kwargs=pool_kwargs
)
else:
# Only scheme should be lower case
conn = self.poolmanager.connection_from_host(
**host_params, pool_kwargs=pool_kwargs
)
return conn
def get_connection(
self, url: str, proxies: dict[str, str] | None = None
) -> HTTPConnectionPool:
"""DEPRECATED: Users should move to `get_connection_with_tls_context`
for all subclasses of HTTPAdapter using Requests>=2.32.2.
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 <requests.adapters.HTTPAdapter>`.
:param url: The URL to connect to.
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
:rtype: urllib3.ConnectionPool
:rtype: urllib3.HTTPConnectionPool
"""
warnings.warn(
(
"`get_connection` has been deprecated in favor of "
"`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
"will need to migrate for Requests>=2.32.2. Please see "
"https://github.com/psf/requests/pull/6710 for more details."
),
DeprecationWarning,
)
proxy = select_proxy(url, proxies)
if proxy:
proxy = prepend_scheme_if_needed(proxy, 'http')
proxy = prepend_scheme_if_needed(proxy, "http")
proxy_url = parse_url(proxy)
if not proxy_url.host:
raise InvalidProxyURL("Please check proxy URL. It is malformed"
" and could be missing the host.")
raise InvalidProxyURL(
"Please check proxy URL. It is malformed "
"and could be missing the host."
)
proxy_manager = self.proxy_manager_for(proxy)
conn = proxy_manager.connection_from_url(url)
else:
@@ -317,7 +545,7 @@ class HTTPAdapter(BaseAdapter):
return conn
def close(self):
def close(self) -> None:
"""Disposes of any internal state.
Currently, this closes the PoolManager and any active ProxyManager,
@@ -327,7 +555,9 @@ class HTTPAdapter(BaseAdapter):
for proxy in self.proxy_manager.values():
proxy.clear()
def request_url(self, request, proxies):
def request_url(
self, request: PreparedRequest, proxies: dict[str, str] | None
) -> str:
"""Obtain the url to use when making the final request.
If the message is being sent through a HTTP proxy, the full URL has to
@@ -341,22 +571,27 @@ class HTTPAdapter(BaseAdapter):
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
:rtype: str
"""
assert _is_prepared(request)
proxy = select_proxy(request.url, proxies)
scheme = urlparse(request.url).scheme
is_proxied_http_request = (proxy and scheme != 'https')
is_proxied_http_request = proxy and scheme != "https"
using_socks_proxy = False
if proxy:
proxy_scheme = urlparse(proxy).scheme.lower()
using_socks_proxy = proxy_scheme.startswith('socks')
using_socks_proxy = proxy_scheme.startswith("socks")
url = request.path_url
if url.startswith("//"): # Don't confuse urllib3
url = f"/{url.lstrip('/')}"
if is_proxied_http_request and not using_socks_proxy:
url = urldefragauth(request.url)
return url
def add_headers(self, request, **kwargs):
def add_headers(self, request: PreparedRequest, **kwargs: Any) -> None:
"""Add any headers needed by the connection. As of v2.0 this does
nothing by default, but is left for overriding by users that subclass
the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
@@ -370,7 +605,7 @@ class HTTPAdapter(BaseAdapter):
"""
pass
def proxy_headers(self, proxy):
def proxy_headers(self, proxy: str) -> dict[str, str]:
"""Returns a dictionary of the headers to add to any request sent
through a proxy. This works with urllib3 magic to ensure that they are
correctly sent to the proxy, rather than in a tunnelled request if
@@ -383,16 +618,23 @@ class HTTPAdapter(BaseAdapter):
:param proxy: The url of the proxy being used for this request.
:rtype: dict
"""
headers = {}
headers: dict[str, str] = {}
username, password = get_auth_from_url(proxy)
if username:
headers['Proxy-Authorization'] = _basic_auth_str(username,
password)
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
return headers
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
def send(
self,
request: PreparedRequest,
stream: bool = False,
timeout: _t.TimeoutType = None,
verify: _t.VerifyType = True,
cert: _t.CertType = None,
proxies: dict[str, str] | None = None,
) -> Response:
"""Sends PreparedRequest object. Returns Response object.
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
@@ -409,95 +651,58 @@ class HTTPAdapter(BaseAdapter):
:rtype: requests.Response
"""
assert _is_prepared(request)
try:
conn = self.get_connection(request.url, proxies)
conn = self.get_connection_with_tls_context(
request, verify, proxies=proxies, cert=cert
)
except LocationValueError as e:
raise InvalidURL(e, request=request)
self.cert_verify(conn, request.url, verify, cert)
url = self.request_url(request, proxies)
self.add_headers(request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
self.add_headers(
request,
stream=stream,
timeout=timeout,
verify=verify,
cert=cert,
proxies=proxies,
)
chunked = not (request.body is None or 'Content-Length' in request.headers)
chunked = not (request.body is None or "Content-Length" in request.headers)
if isinstance(timeout, tuple):
try:
connect, read = timeout
timeout = TimeoutSauce(connect=connect, read=read)
except ValueError as e:
# this may raise a string formatting error.
err = ("Invalid timeout {}. Pass a (connect, read) "
"timeout tuple, or a single float to set "
"both timeouts to the same value".format(timeout))
raise ValueError(err)
resolved_timeout = TimeoutSauce(connect=connect, read=read)
except ValueError:
raise ValueError(
f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
f"or a single float to set both timeouts to the same value."
)
elif isinstance(timeout, TimeoutSauce):
pass
resolved_timeout = timeout
else:
timeout = TimeoutSauce(connect=timeout, read=timeout)
resolved_timeout = TimeoutSauce(connect=timeout, read=timeout)
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
)
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str]
headers=request.headers, # type: ignore[arg-type] # urllib3#3072
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,
retries=self.max_retries,
timeout=resolved_timeout,
chunked=chunked,
)
# Send the request.
else:
if hasattr(conn, 'proxy_pool'):
conn = conn.proxy_pool
low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
try:
skip_host = 'Host' in request.headers
low_conn.putrequest(request.method,
url,
skip_accept_encoding=True,
skip_host=skip_host)
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')
# Receive the response from the server
try:
# For Python 2.7, use buffering of HTTP responses
r = low_conn.getresponse(buffering=True)
except TypeError:
# For compatibility with Python 3.3+
r = low_conn.getresponse()
resp = HTTPResponse.from_httplib(
r,
pool=conn,
connection=low_conn,
preload_content=False,
decode_content=False
)
except:
# If we hit any problems here, clean up the connection.
# Then, reraise so that we can handle the actual exception.
low_conn.close()
raise
except (ProtocolError, socket.error) as err:
except (ProtocolError, OSError) as err:
raise ConnectionError(err, request=request)
except MaxRetryError as e:

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
requests.api
~~~~~~~~~~~~
@@ -10,10 +8,22 @@ This module implements the Requests API.
:license: Apache2, see LICENSE for more details.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from . import sessions
from .models import Response
if TYPE_CHECKING:
from typing_extensions import Unpack
from . import _types as _t
def request(method, url, **kwargs):
def request(
method: str, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]
) -> Response:
"""Constructs and sends a :class:`Request <Request>`.
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
@@ -27,7 +37,7 @@ def request(method, url, **kwargs):
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
to add for the file.
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
@@ -61,7 +71,9 @@ def request(method, url, **kwargs):
return session.request(method=method, url=url, **kwargs)
def get(url, params=None, **kwargs):
def get(
url: _t.UriType, params: _t.ParamsType = None, **kwargs: Unpack[_t.GetKwargs]
) -> Response:
r"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
@@ -72,10 +84,10 @@ def get(url, params=None, **kwargs):
:rtype: requests.Response
"""
return request('get', url, params=params, **kwargs)
return request("get", url, params=params, **kwargs)
def options(url, **kwargs):
def options(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends an OPTIONS request.
:param url: URL for the new :class:`Request` object.
@@ -84,10 +96,10 @@ def options(url, **kwargs):
:rtype: requests.Response
"""
return request('options', url, **kwargs)
return request("options", url, **kwargs)
def head(url, **kwargs):
def head(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends a HEAD request.
:param url: URL for the new :class:`Request` object.
@@ -98,56 +110,65 @@ def head(url, **kwargs):
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', False)
return request('head', url, **kwargs)
kwargs.setdefault("allow_redirects", False)
return request("head", url, **kwargs)
def post(url, data=None, json=None, **kwargs):
def post(
url: _t.UriType,
data: _t.DataType = None,
json: _t.JsonType = None,
**kwargs: Unpack[_t.PostKwargs],
) -> Response:
r"""Sends a POST request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('post', url, data=data, json=json, **kwargs)
return request("post", url, data=data, json=json, **kwargs)
def put(url, data=None, **kwargs):
def put(
url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]
) -> Response:
r"""Sends a PUT request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('put', url, data=data, **kwargs)
return request("put", url, data=data, **kwargs)
def patch(url, data=None, **kwargs):
def patch(
url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]
) -> Response:
r"""Sends a PATCH request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('patch', url, data=data, **kwargs)
return request("patch", url, data=data, **kwargs)
def delete(url, **kwargs):
def delete(url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends a DELETE request.
:param url: URL for the new :class:`Request` object.
@@ -156,4 +177,4 @@ def delete(url, **kwargs):
:rtype: requests.Response
"""
return request('delete', url, **kwargs)
return request("delete", url, **kwargs)

342
src/requests/auth.py Normal file
View File

@@ -0,0 +1,342 @@
"""
requests.auth
~~~~~~~~~~~~~
This module contains the authentication handlers for Requests.
"""
from __future__ import annotations
import hashlib
import os
import re
import threading
import time
import warnings
from base64 import b64encode
from typing import TYPE_CHECKING, Any, Final, cast, overload
from ._internal_utils import to_native_string
from .compat import basestring, str, urlparse
from .cookies import extract_cookies_to_jar
from .utils import parse_dict_header
if TYPE_CHECKING:
from http.cookiejar import CookieJar
from typing import Any
from .models import PreparedRequest, Response
CONTENT_TYPE_FORM_URLENCODED: Final = "application/x-www-form-urlencoded"
CONTENT_TYPE_MULTI_PART: Final = "multipart/form-data"
def _basic_auth_str(username: bytes | str, password: bytes | str) -> str:
"""Returns a Basic Auth string."""
# "I want us to put a big-ol' comment on top of it that
# says that this behaviour is dumb but we need to preserve
# it because people are relying on it."
# - Lukasa
#
# These are here solely to maintain backwards compatibility
# for things like ints. This will be removed in 3.0.0.
if not isinstance(username, basestring): # type: ignore[reportUnnecessaryIsInstance] # runtime guard for non-str/bytes
warnings.warn(
"Non-string usernames will no longer be supported in Requests "
f"3.0.0. Please convert the object you've passed in ({username!r}) to "
"a string or bytes object in the near future to avoid "
"problems.",
category=DeprecationWarning,
)
username = str(username)
if not isinstance(password, basestring): # type: ignore[reportUnnecessaryIsInstance] # runtime guard for non-str/bytes
warnings.warn(
"Non-string passwords will no longer be supported in Requests "
f"3.0.0. Please convert the object you've passed in ({type(password)!r}) to "
"a string or bytes object in the near future to avoid "
"problems.",
category=DeprecationWarning,
)
password = str(password)
# -- End Removal --
if isinstance(username, str):
username = username.encode("latin1")
if isinstance(password, str):
password = password.encode("latin1")
authstr = "Basic " + to_native_string(
b64encode(b":".join((username, password))).strip()
)
return authstr
class AuthBase:
"""Base class that all auth implementations derive from"""
def __call__(self, r: PreparedRequest) -> PreparedRequest:
raise NotImplementedError("Auth hooks must be callable.")
class HTTPBasicAuth(AuthBase):
"""Attaches HTTP Basic Authentication to the given Request object."""
@overload
def __init__(self, username: str, password: str) -> None: ...
@overload
def __init__(self, username: bytes, password: bytes) -> None: ...
def __init__(self, username: bytes | str, password: bytes | str) -> None:
self.username: bytes | str = username
self.password: bytes | str = password
def __eq__(self, other: object) -> bool:
return all(
[
self.username == getattr(other, "username", None),
self.password == getattr(other, "password", None),
]
)
def __ne__(self, other: Any) -> bool:
return not self == other
def __call__(self, r: PreparedRequest) -> PreparedRequest:
r.headers["Authorization"] = _basic_auth_str(self.username, self.password)
return r
class HTTPProxyAuth(HTTPBasicAuth):
"""Attaches HTTP Proxy Authentication to a given Request object."""
def __call__(self, r: PreparedRequest) -> PreparedRequest:
r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password)
return r
class HTTPDigestAuth(AuthBase):
"""Attaches HTTP Digest Authentication to the given Request object."""
@overload
def __init__(self, username: str, password: str) -> None: ...
@overload
def __init__(self, username: bytes, password: bytes) -> None: ...
def __init__(self, username: bytes | str, password: bytes | str) -> None:
self.username: bytes | str = username
self.password: bytes | str = password
# Keep state in per-thread local storage
self._thread_local: threading.local = threading.local()
def init_per_thread_state(self) -> None:
# Ensure state is initialized just once per-thread
if not hasattr(self._thread_local, "init"):
self._thread_local.init = True
self._thread_local.last_nonce = ""
self._thread_local.nonce_count = 0
self._thread_local.chal = {}
self._thread_local.pos = None
self._thread_local.num_401_calls = None
def build_digest_header(self, method: str, url: str) -> str | None:
"""
:rtype: str
"""
realm = self._thread_local.chal["realm"]
nonce = self._thread_local.chal["nonce"]
qop = self._thread_local.chal.get("qop")
algorithm = self._thread_local.chal.get("algorithm")
opaque = self._thread_local.chal.get("opaque")
hash_utf8 = None
if algorithm is None:
_algorithm = "MD5"
else:
_algorithm = algorithm.upper()
# lambdas assume digest modules are imported at the top level
if _algorithm == "MD5" or _algorithm == "MD5-SESS":
def md5_utf8(x: str | bytes) -> str:
if isinstance(x, str):
x = x.encode("utf-8")
return hashlib.md5(x, usedforsecurity=False).hexdigest()
hash_utf8 = md5_utf8
elif _algorithm == "SHA":
def sha_utf8(x: str | bytes) -> str:
if isinstance(x, str):
x = x.encode("utf-8")
return hashlib.sha1(x, usedforsecurity=False).hexdigest()
hash_utf8 = sha_utf8
elif _algorithm == "SHA-256":
def sha256_utf8(x: str | bytes) -> str:
if isinstance(x, str):
x = x.encode("utf-8")
return hashlib.sha256(x, usedforsecurity=False).hexdigest()
hash_utf8 = sha256_utf8
elif _algorithm == "SHA-512":
def sha512_utf8(x: str | bytes) -> str:
if isinstance(x, str):
x = x.encode("utf-8")
return hashlib.sha512(x, usedforsecurity=False).hexdigest()
hash_utf8 = sha512_utf8
if hash_utf8 is None:
return None
def KD(s: str, d: str) -> str:
return hash_utf8(f"{s}:{d}")
# XXX not implemented yet
entdig = None
p_parsed = urlparse(url)
#: path is request-uri defined in RFC 2616 which should not be empty
path = p_parsed.path or "/"
if p_parsed.query:
path += f"?{p_parsed.query}"
A1 = f"{self.username}:{realm}:{self.password}"
A2 = f"{method}:{path}"
HA1 = hash_utf8(A1)
HA2 = hash_utf8(A2)
if nonce == self._thread_local.last_nonce:
self._thread_local.nonce_count += 1
else:
self._thread_local.nonce_count = 1
ncvalue = f"{self._thread_local.nonce_count:08x}"
s = str(self._thread_local.nonce_count).encode("utf-8")
s += nonce.encode("utf-8")
s += time.ctime().encode("utf-8")
s += os.urandom(8)
cnonce = hashlib.sha1(s, usedforsecurity=False).hexdigest()[:16]
if _algorithm == "MD5-SESS":
HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}") # type: ignore[reportConstantRedefinition] # RFC 2617 terminology
if not qop:
respdig = KD(HA1, f"{nonce}:{HA2}")
elif qop == "auth" or "auth" in qop.split(","):
noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}"
respdig = KD(HA1, noncebit)
else:
# XXX handle auth-int.
return None
self._thread_local.last_nonce = nonce
# XXX should the partial digests be encoded too?
base = (
f'username="{self.username}", realm="{realm}", nonce="{nonce}", '
f'uri="{path}", response="{respdig}"'
)
if opaque:
base += f', opaque="{opaque}"'
if algorithm:
base += f', algorithm="{algorithm}"'
if entdig:
base += f', digest="{entdig}"'
if qop:
base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"'
return f"Digest {base}"
def handle_redirect(self, r: Response, **kwargs: Any) -> None:
"""Reset num_401_calls counter on redirects."""
if r.is_redirect:
self._thread_local.num_401_calls = 1
def handle_401(self, r: Response, **kwargs: Any) -> Response:
"""
Takes the given response and tries digest-auth, if needed.
:rtype: requests.Response
"""
# If response is not 4xx, do not auth
# See https://github.com/psf/requests/issues/3772
if not 400 <= r.status_code < 500:
self._thread_local.num_401_calls = 1
return r
if self._thread_local.pos is not None:
# Rewind the file position indicator of the body to where
# it was to resend the request.
if (seek := getattr(r.request.body, "seek", None)) is not None:
seek(self._thread_local.pos)
s_auth = r.headers.get("www-authenticate", "")
if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
self._thread_local.num_401_calls += 1
pat = re.compile(r"digest ", flags=re.IGNORECASE)
self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))
# Consume content and release the original connection
# to allow our new request to reuse the same one.
r.content
r.close()
prep = r.request.copy()
cookie_jar = cast("CookieJar", prep._cookies) # type: ignore[reportPrivateUsage]
extract_cookies_to_jar(cookie_jar, r.request, r.raw)
prep.prepare_cookies(cookie_jar)
_digest_auth = self.build_digest_header(
cast(str, prep.method), cast(str, prep.url)
)
if _digest_auth:
prep.headers["Authorization"] = _digest_auth
_r = r.connection.send(prep, **kwargs)
_r.history.append(r)
_r.request = prep
return _r
self._thread_local.num_401_calls = 1
return r
def __call__(self, r: PreparedRequest) -> PreparedRequest:
# Initialize per-thread state, if needed
self.init_per_thread_state()
# If we have a saved nonce, skip the 401
if self._thread_local.last_nonce:
_digest_auth = self.build_digest_header(
cast(str, r.method), cast(str, r.url)
)
if _digest_auth:
r.headers["Authorization"] = _digest_auth
if (tell := getattr(r.body, "tell", None)) is not None:
self._thread_local.pos = tell()
else:
# In the case of HTTPDigestAuth being reused and the body of
# the previous request was a file-like object, pos has the
# file position of the previous body. Ensure it's set to
# None.
self._thread_local.pos = None
r.register_hook("response", self.handle_401)
r.register_hook("response", self.handle_redirect)
self._thread_local.num_401_calls = 1
return r
def __eq__(self, other: object) -> bool:
return all(
[
self.username == getattr(other, "username", None),
self.password == getattr(other, "password", None),
]
)
def __ne__(self, other: Any) -> bool:
return not self == other

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
requests.certs
@@ -12,7 +11,8 @@ 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.
"""
from certifi import where
if __name__ == '__main__':
if __name__ == "__main__":
print(where())

113
src/requests/compat.py Normal file
View File

@@ -0,0 +1,113 @@
"""
requests.compat
~~~~~~~~~~~~~~~
This module previously handled import compatibility issues
between Python 2 and Python 3. It remains for backwards
compatibility until the next major version.
"""
# pyright: reportUnusedImport=false
from __future__ import annotations
import importlib
import sys
from types import ModuleType
# -------
# urllib3
# -------
from urllib3 import (
__version__ as urllib3_version, # type: ignore[reportPrivateImportUsage]
)
# Detect which major version of urllib3 is being used.
try:
is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1
except (TypeError, AttributeError):
# If we can't discern a version, prefer old functionality.
is_urllib3_1 = True
# -------------------
# Character Detection
# -------------------
def _resolve_char_detection() -> ModuleType | None:
"""Find supported character detection libraries."""
chardet = None
for lib in ("chardet", "charset_normalizer"):
if chardet is None:
try:
chardet = importlib.import_module(lib)
except ImportError:
pass
return chardet
chardet = _resolve_char_detection()
# -------
# Pythons
# -------
# Syntax sugar.
_ver = sys.version_info
#: Python 2.x?
is_py2 = _ver[0] == 2
#: Python 3.x?
is_py3 = _ver[0] == 3
# json/simplejson module import resolution
has_simplejson = False
try:
import simplejson as json # type: ignore[import-not-found]
has_simplejson = True
except ImportError:
import json
if has_simplejson:
from simplejson import JSONDecodeError # type: ignore[import-not-found]
else:
from json import JSONDecodeError
# Keep OrderedDict for backwards compatibility.
from collections import OrderedDict
from collections.abc import Callable, Mapping, MutableMapping
from http import cookiejar as cookielib
from http.cookies import Morsel
from io import StringIO
# --------------
# Legacy Imports
# --------------
from urllib.parse import (
quote,
quote_plus,
unquote,
unquote_plus,
urldefrag,
urlencode,
urljoin,
urlparse,
urlsplit,
urlunparse,
)
from urllib.request import (
getproxies,
getproxies_environment,
parse_http_list,
proxy_bypass,
proxy_bypass_environment, # type: ignore[attr-defined] # https://github.com/python/cpython/issues/145331
)
builtin_str = str
str = str
bytes = bytes
basestring = (str, bytes)
numeric_types = (int, float)
integer_types = (int,)

View File

@@ -1,31 +1,37 @@
# -*- coding: utf-8 -*-
"""
requests.cookies
~~~~~~~~~~~~~~~~
Compatibility code to be able to use `cookielib.CookieJar` with requests.
Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
requests.utils imports from here, so be careful with imports.
"""
from __future__ import annotations
import calendar
import copy
import time
import calendar
from collections.abc import Iterator, MutableMapping
from http.cookiejar import Cookie, CookieJar, CookiePolicy
from typing import TYPE_CHECKING, Any, TypeVar, overload
from ._internal_utils import to_native_string
from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping
from ._types import is_prepared as _is_prepared
from .compat import Morsel, cookielib, urlparse, urlunparse
try:
import threading
except ImportError:
import dummy_threading as threading
if TYPE_CHECKING:
from _typeshed import SupportsKeysAndGetItem
from .models import PreparedRequest
import threading
class MockRequest(object):
"""Wraps a `requests.Request` to mimic a `urllib2.Request`.
class MockRequest:
"""Wraps a `requests.PreparedRequest` to mimic a `urllib2.Request`.
The code in `cookielib.CookieJar` expects this interface in order to correctly
The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
manage cookie policies, i.e., determine whether a cookie can be set, given the
domains of the request and the cookie.
@@ -34,121 +40,133 @@ class MockRequest(object):
probably want `get_cookie_header`, defined below.
"""
def __init__(self, request):
def __init__(self, request: PreparedRequest) -> None:
assert _is_prepared(request)
self._r = request
self._new_headers = {}
self.type = urlparse(self._r.url).scheme
self._new_headers: dict[str, str] = {}
self.type: str = urlparse(self._r.url).scheme
def get_type(self):
def get_type(self) -> str:
return self.type
def get_host(self):
def get_host(self) -> str:
return urlparse(self._r.url).netloc
def get_origin_req_host(self):
def get_origin_req_host(self) -> str:
return self.get_host()
def get_full_url(self):
def get_full_url(self) -> str:
# Only return the response's URL if the user hadn't set the Host
# header
if not self._r.headers.get('Host'):
if not self._r.headers.get("Host"):
return self._r.url
# If they did set it, retrieve it and reconstruct the expected domain
host = to_native_string(self._r.headers['Host'], encoding='utf-8')
host = to_native_string(self._r.headers["Host"], encoding="utf-8")
parsed = urlparse(self._r.url)
# Reconstruct the URL as we expect it
return urlunparse([
parsed.scheme, host, parsed.path, parsed.params, parsed.query,
parsed.fragment
])
return urlunparse(
[
parsed.scheme,
host,
parsed.path,
parsed.params,
parsed.query,
parsed.fragment,
]
)
def is_unverifiable(self):
def is_unverifiable(self) -> bool:
return True
def has_header(self, name):
def has_header(self, name: str) -> bool:
return name in self._r.headers or name in self._new_headers
def get_header(self, name, default=None):
return self._r.headers.get(name, self._new_headers.get(name, default))
def get_header(self, name: str, default: str | None = None) -> str | None:
return self._r.headers.get(name, self._new_headers.get(name, default)) # type: ignore[return-value]
def add_header(self, key, val):
"""cookielib has no legitimate use for this method; add it back if you find one."""
raise NotImplementedError("Cookie headers should be added with add_unredirected_header()")
def add_header(self, key: str, val: str) -> None:
"""cookiejar has no legitimate use for this method; add it back if you find one."""
raise NotImplementedError(
"Cookie headers should be added with add_unredirected_header()"
)
def add_unredirected_header(self, name, value):
def add_unredirected_header(self, name: str, value: str) -> None:
self._new_headers[name] = value
def get_new_headers(self):
def get_new_headers(self) -> dict[str, str]:
return self._new_headers
@property
def unverifiable(self):
def unverifiable(self) -> bool:
return self.is_unverifiable()
@property
def origin_req_host(self):
def origin_req_host(self) -> str:
return self.get_origin_req_host()
@property
def host(self):
def host(self) -> str:
return self.get_host()
class MockResponse(object):
class MockResponse:
"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
...what? Basically, expose the parsed HTTP headers from the server response
the way `cookielib` expects to see them.
the way `http.cookiejar` expects to see them.
"""
def __init__(self, headers):
"""Make a MockResponse for `cookielib` to read.
def __init__(self, headers: Any) -> None:
"""Make a MockResponse for `cookiejar` to read.
:param headers: a httplib.HTTPMessage or analogous carrying the headers
"""
self._headers = headers
def info(self):
def info(self) -> Any:
return self._headers
def getheaders(self, name):
def getheaders(self, name: str) -> Any:
self._headers.getheaders(name)
def extract_cookies_to_jar(jar, request, response):
def extract_cookies_to_jar(
jar: CookieJar, request: PreparedRequest, response: Any
) -> None:
"""Extract the cookies from the response into a CookieJar.
:param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar)
:param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
:param request: our own requests.Request object
:param response: urllib3.HTTPResponse object
"""
if not (hasattr(response, '_original_response') and
response._original_response):
if not (hasattr(response, "_original_response") and response._original_response):
return
# the _original_response field is the wrapped httplib.HTTPResponse object,
req = MockRequest(request)
# pull out the HTTPMessage with the headers and put it in the mock:
res = MockResponse(response._original_response.msg)
jar.extract_cookies(res, req)
jar.extract_cookies(res, req) # type: ignore[arg-type]
def get_cookie_header(jar, request):
def get_cookie_header(jar: CookieJar, request: PreparedRequest) -> str | None:
"""
Produce an appropriate Cookie header string to be sent with `request`, or None.
:rtype: str
"""
r = MockRequest(request)
jar.add_cookie_header(r)
return r.get_new_headers().get('Cookie')
jar.add_cookie_header(r) # type: ignore[arg-type]
return r.get_new_headers().get("Cookie")
def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
def remove_cookie_by_name(
cookiejar: CookieJar, name: str, domain: str | None = None, path: str | None = None
) -> None:
"""Unsets a cookie by name, by default over all domains and paths.
Wraps CookieJar.clear(), is O(n).
"""
clearables = []
clearables: list[tuple[str, str, str]] = []
for cookie in cookiejar:
if cookie.name != name:
continue
@@ -168,8 +186,8 @@ class CookieConflictError(RuntimeError):
"""
class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
"""Compatibility class; is a cookielib.CookieJar, but exposes a dict
class RequestsCookieJar(CookieJar, MutableMapping[str, str | None]): # type: ignore[misc]
"""Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
interface.
This is the CookieJar we create by default for requests and sessions that
@@ -186,7 +204,15 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. warning:: dictionary operations that are normally O(1) may be O(n).
"""
def get(self, name, default=None, domain=None, path=None):
_policy: CookiePolicy
def get( # type: ignore[override]
self,
name: str,
default: str | None = None,
domain: str | None = None,
path: str | None = None,
) -> str | None:
"""Dict-like get() that also supports optional domain and path args in
order to resolve naming collisions from using one cookie jar over
multiple domains.
@@ -198,14 +224,18 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
except KeyError:
return default
def set(self, name, value, **kwargs):
def set(
self, name: str, value: str | Morsel[dict[str, str]] | None, **kwargs: Any
) -> Cookie | None:
"""Dict-like set() that also supports optional domain and path args in
order to resolve naming collisions from using one cookie jar over
multiple domains.
"""
# support client code that unsets cookies by assignment of a None value:
if value is None:
remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path'))
remove_cookie_by_name(
self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
)
return
if isinstance(value, Morsel):
@@ -215,7 +245,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
self.set_cookie(c)
return c
def iterkeys(self):
def iterkeys(self) -> Iterator[str]:
"""Dict-like iterkeys() that returns an iterator of names of cookies
from the jar.
@@ -224,7 +254,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
for cookie in iter(self):
yield cookie.name
def keys(self):
def keys(self) -> list[str]: # type: ignore[override]
"""Dict-like keys() that returns a list of names of cookies from the
jar.
@@ -232,7 +262,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
"""
return list(self.iterkeys())
def itervalues(self):
def itervalues(self) -> Iterator[str | None]:
"""Dict-like itervalues() that returns an iterator of values of cookies
from the jar.
@@ -241,7 +271,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
for cookie in iter(self):
yield cookie.value
def values(self):
def values(self) -> list[str | None]: # type: ignore[override]
"""Dict-like values() that returns a list of values of cookies from the
jar.
@@ -249,7 +279,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
"""
return list(self.itervalues())
def iteritems(self):
def iteritems(self) -> Iterator[tuple[str, str | None]]:
"""Dict-like iteritems() that returns an iterator of name-value tuples
from the jar.
@@ -258,7 +288,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
for cookie in iter(self):
yield cookie.name, cookie.value
def items(self):
def items(self) -> list[tuple[str, str | None]]: # type: ignore[override]
"""Dict-like items() that returns a list of name-value tuples from the
jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
vanilla python dict of key value pairs.
@@ -267,58 +297,63 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
"""
return list(self.iteritems())
def list_domains(self):
def list_domains(self) -> list[str]:
"""Utility method to list all the domains in the jar."""
domains = []
domains: list[str] = []
for cookie in iter(self):
if cookie.domain not in domains:
domains.append(cookie.domain)
return domains
def list_paths(self):
def list_paths(self) -> list[str]:
"""Utility method to list all the paths in the jar."""
paths = []
paths: list[str] = []
for cookie in iter(self):
if cookie.path not in paths:
paths.append(cookie.path)
return paths
def multiple_domains(self):
def multiple_domains(self) -> bool:
"""Returns True if there are multiple domains in the jar.
Returns False otherwise.
:rtype: bool
"""
domains = []
domains: list[str] = []
for cookie in iter(self):
if cookie.domain is not None and cookie.domain in domains:
if cookie.domain is not None and cookie.domain in domains: # type: ignore[reportUnnecessaryComparison] # defensive check
return True
domains.append(cookie.domain)
return False # there is only one domain in jar
def get_dict(self, domain=None, path=None):
def get_dict(
self, domain: str | None = None, path: str | None = None
) -> dict[str, str | None]:
"""Takes as an argument an optional domain and path and returns a plain
old Python dict of name-value pairs of cookies that meet the
requirements.
:rtype: dict
"""
dictionary = {}
dictionary: dict[str, str | None] = {}
for cookie in iter(self):
if (
(domain is None or cookie.domain == domain) and
(path is None or cookie.path == path)
if (domain is None or cookie.domain == domain) and (
path is None or cookie.path == path
):
dictionary[cookie.name] = cookie.value
return dictionary
def __contains__(self, name):
def __iter__(self) -> Iterator[Cookie]: # type: ignore[override]
"""RequestCookieJar's __iter__ comes from CookieJar not MutableMapping."""
return super().__iter__()
def __contains__(self, name: object) -> bool:
try:
return super(RequestsCookieJar, self).__contains__(name)
return super().__contains__(name)
except CookieConflictError:
return True
def __getitem__(self, name):
def __getitem__(self, name: str) -> str | None:
"""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.
@@ -327,33 +362,43 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
"""
return self._find_no_duplicates(name)
def __setitem__(self, name, value):
def __setitem__(
self, name: str, value: str | Morsel[dict[str, str]] | None
) -> None:
"""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
def __delitem__(self, name: str) -> None:
"""Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
``remove_cookie_by_name()``.
"""
remove_cookie_by_name(self, name)
def set_cookie(self, cookie, *args, **kwargs):
if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'):
cookie.value = cookie.value.replace('\\"', '')
return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs)
def set_cookie(self, cookie: Cookie, *args: Any, **kwargs: Any) -> None:
if (
(value := cookie.value) is not None
and value.startswith('"')
and value.endswith('"')
):
cookie.value = value.replace('\\"', "")
return super().set_cookie(cookie, *args, **kwargs)
def update(self, other):
def update( # type: ignore[override]
self, other: CookieJar | SupportsKeysAndGetItem[str, str]
) -> None:
"""Updates this jar with cookies from another CookieJar or dict-like"""
if isinstance(other, cookielib.CookieJar):
for cookie in other:
self.set_cookie(copy.copy(cookie))
else:
super(RequestsCookieJar, self).update(other)
super().update(other)
def _find(self, name, domain=None, path=None):
def _find(
self, name: str, domain: str | None = None, path: str | None = None
) -> str | None:
"""Requests uses this method internally to get cookie values.
If there are conflicting cookies, _find arbitrarily chooses one.
@@ -371,9 +416,11 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
if path is None or cookie.path == path:
return cookie.value
raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))
raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
def _find_no_duplicates(self, name, domain=None, path=None):
def _find_no_duplicates(
self, name: str, domain: str | None = None, path: str | None = None
) -> str:
"""Both ``__get_item__`` and ``get`` call this function: it's never
used elsewhere in Requests.
@@ -390,46 +437,50 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
if cookie.name == name:
if domain is None or cookie.domain == domain:
if path is None or cookie.path == path:
if toReturn is not None: # if there are multiple cookies that meet passed in criteria
raise CookieConflictError('There are multiple cookies with name, %r' % (name))
toReturn = cookie.value # we will eventually return this as long as no cookie conflict
if toReturn is not None:
# if there are multiple cookies that meet passed in criteria
raise CookieConflictError(
f"There are multiple cookies with name, {name!r}"
)
# we will eventually return this as long as no cookie conflict
toReturn = cookie.value
if toReturn:
if toReturn is not None:
return toReturn
raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path))
raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
def __getstate__(self):
def __getstate__(self) -> dict[str, Any]:
"""Unlike a normal CookieJar, this class is pickleable."""
state = self.__dict__.copy()
# remove the unpickleable RLock object
state.pop('_cookies_lock')
state.pop("_cookies_lock")
return state
def __setstate__(self, state):
def __setstate__(self, state: dict[str, Any]) -> None:
"""Unlike a normal CookieJar, this class is pickleable."""
self.__dict__.update(state)
if '_cookies_lock' not in self.__dict__:
if "_cookies_lock" not in self.__dict__:
self._cookies_lock = threading.RLock()
def copy(self):
def copy(self) -> RequestsCookieJar:
"""Return a copy of this RequestsCookieJar."""
new_cj = RequestsCookieJar()
new_cj.set_policy(self.get_policy())
new_cj.update(self)
return new_cj
def get_policy(self):
def get_policy(self) -> CookiePolicy:
"""Return the CookiePolicy instance used."""
return self._policy
def _copy_cookie_jar(jar):
def _copy_cookie_jar(jar: CookieJar | None) -> CookieJar | None: # type: ignore[reportUnusedFunction] # cross-module usage in models.py
if jar is None:
return None
if hasattr(jar, 'copy'):
if copy_method := getattr(jar, "copy", None):
# We're dealing with an instance of RequestsCookieJar
return jar.copy()
return copy_method()
# We're dealing with a generic CookieJar instance
new_jar = copy.copy(jar)
new_jar.clear()
@@ -438,74 +489,96 @@ def _copy_cookie_jar(jar):
return new_jar
def create_cookie(name, value, **kwargs):
def create_cookie(name: str, value: str, **kwargs: Any) -> Cookie:
"""Make a cookie from underspecified parameters.
By default, the pair of `name` and `value` will be set for the domain ''
and sent on every request (this is sometimes called a "supercookie").
"""
result = {
'version': 0,
'name': name,
'value': value,
'port': None,
'domain': '',
'path': '/',
'secure': False,
'expires': None,
'discard': True,
'comment': None,
'comment_url': None,
'rest': {'HttpOnly': None},
'rfc2109': False,
result: dict[str, Any] = {
"version": 0,
"name": name,
"value": value,
"port": None,
"domain": "",
"path": "/",
"secure": False,
"expires": None,
"discard": True,
"comment": None,
"comment_url": None,
"rest": {"HttpOnly": None},
"rfc2109": False,
}
badargs = set(kwargs) - set(result)
if badargs:
err = 'create_cookie() got unexpected keyword arguments: %s'
raise TypeError(err % list(badargs))
raise TypeError(
f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
)
result.update(kwargs)
result['port_specified'] = bool(result['port'])
result['domain_specified'] = bool(result['domain'])
result['domain_initial_dot'] = result['domain'].startswith('.')
result['path_specified'] = bool(result['path'])
result["port_specified"] = bool(result["port"])
result["domain_specified"] = bool(result["domain"])
result["domain_initial_dot"] = result["domain"].startswith(".")
result["path_specified"] = bool(result["path"])
return cookielib.Cookie(**result)
def morsel_to_cookie(morsel):
def morsel_to_cookie(morsel: Morsel[Any]) -> Cookie:
"""Convert a Morsel object into a Cookie containing the one k/v pair."""
expires = None
if morsel['max-age']:
expires: int | None = None
if morsel["max-age"]:
try:
expires = int(time.time() + int(morsel['max-age']))
expires = int(time.time() + int(morsel["max-age"]))
except ValueError:
raise TypeError('max-age: %s must be integer' % morsel['max-age'])
elif morsel['expires']:
time_template = '%a, %d-%b-%Y %H:%M:%S GMT'
expires = calendar.timegm(
time.strptime(morsel['expires'], time_template)
)
raise TypeError(f"max-age: {morsel['max-age']} must be integer")
elif morsel["expires"]:
time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
expires = calendar.timegm(time.strptime(morsel["expires"], time_template))
return create_cookie(
comment=morsel['comment'],
comment_url=bool(morsel['comment']),
comment=morsel["comment"],
comment_url=bool(morsel["comment"]),
discard=False,
domain=morsel['domain'],
domain=morsel["domain"],
expires=expires,
name=morsel.key,
path=morsel['path'],
path=morsel["path"],
port=None,
rest={'HttpOnly': morsel['httponly']},
rest={"HttpOnly": morsel["httponly"]},
rfc2109=False,
secure=bool(morsel['secure']),
secure=bool(morsel["secure"]),
value=morsel.value,
version=morsel['version'] or 0,
version=morsel["version"] or 0,
)
def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
_CookieJarT = TypeVar("_CookieJarT", bound=CookieJar)
@overload
def cookiejar_from_dict(
cookie_dict: dict[str, str] | None,
cookiejar: None = None,
overwrite: bool = True,
) -> RequestsCookieJar: ...
@overload
def cookiejar_from_dict(
cookie_dict: dict[str, str] | None,
cookiejar: _CookieJarT,
overwrite: bool = True,
) -> _CookieJarT: ...
def cookiejar_from_dict(
cookie_dict: dict[str, str] | None,
cookiejar: CookieJar | None = None,
overwrite: bool = True,
) -> CookieJar:
"""Returns a CookieJar from a key/value dictionary.
:param cookie_dict: Dict of key/values to insert into CookieJar.
@@ -526,23 +599,24 @@ def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
return cookiejar
def merge_cookies(cookiejar, cookies):
def merge_cookies(
cookiejar: CookieJar, cookies: dict[str, str] | CookieJar | None
) -> CookieJar:
"""Add cookies to cookiejar and returns a merged CookieJar.
:param cookiejar: CookieJar object to add the cookies to.
:param cookies: Dictionary or CookieJar object to be added.
:rtype: CookieJar
"""
if not isinstance(cookiejar, cookielib.CookieJar):
raise ValueError('You can only merge into CookieJar')
if not isinstance(cookiejar, cookielib.CookieJar): # type: ignore[reportUnnecessaryIsInstance] # runtime guard
raise ValueError("You can only merge into CookieJar")
if isinstance(cookies, dict):
cookiejar = cookiejar_from_dict(
cookies, cookiejar=cookiejar, overwrite=False)
cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False)
elif isinstance(cookies, cookielib.CookieJar):
try:
cookiejar.update(cookies)
except AttributeError:
if update_method := getattr(cookiejar, "update", None):
update_method(cookies)
else:
for cookie_in_jar in cookies:
cookiejar.set_cookie(cookie_in_jar)

View File

@@ -1,30 +1,35 @@
# -*- coding: utf-8 -*-
"""
requests.exceptions
~~~~~~~~~~~~~~~~~~~
This module contains the set of Requests' exceptions.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from urllib3.exceptions import HTTPError as BaseHTTPError
from .compat import JSONDecodeError as CompatJSONDecodeError
if TYPE_CHECKING:
from .models import PreparedRequest, Request, Response
class RequestException(IOError):
"""There was an ambiguous exception that occurred while handling your
request.
"""
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize RequestException with `request` and `response` objects."""
response = kwargs.pop('response', None)
self.response = response
self.request = kwargs.pop('request', None)
if (response is not None and not self.request and
hasattr(response, 'request')):
self.request = self.response.request
super(RequestException, self).__init__(*args, **kwargs)
response: Response | None = kwargs.pop("response", None)
self.response: Response | None = response
self.request: Request | PreparedRequest | None = kwargs.pop("request", None)
if response is not None and not self.request and hasattr(response, "request"):
self.request = response.request
super().__init__(*args, **kwargs)
class InvalidJSONError(RequestException):
@@ -34,6 +39,26 @@ class InvalidJSONError(RequestException):
class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
"""Couldn't decode the text into json"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
Construct the JSONDecodeError instance first with all
args. Then use it's args to construct the IOError so that
the json specific args aren't used as IOError specific args
and the error message from JSONDecodeError is preserved.
"""
CompatJSONDecodeError.__init__(self, *args)
InvalidJSONError.__init__(self, *self.args, **kwargs)
def __reduce__(self) -> tuple[Any, ...] | str:
"""
The __reduce__ method called when pickling the object must
be the one from the JSONDecodeError (be it json/simplejson)
as it expects all the arguments for instantiation, not just
one like the IOError, and the MRO would by default call the
__reduce__ method from the IOError due to the inheritance order.
"""
return CompatJSONDecodeError.__reduce__(self)
class HTTPError(RequestException):
"""An HTTP error occurred."""
@@ -118,6 +143,7 @@ class RetryError(RequestException):
class UnrewindableBodyError(RequestException):
"""Requests encountered an error when trying to rewind a body."""
# Warnings

134
src/requests/help.py Normal file
View File

@@ -0,0 +1,134 @@
"""Module containing bug report helper(s)."""
# pyright: reportUnknownMemberType=false
import json
import platform
import ssl
import sys
from typing import Any
import idna
import urllib3
from . import __version__ as requests_version
try:
import charset_normalizer
except ImportError:
charset_normalizer = None
try:
import chardet # type: ignore[import-not-found]
except ImportError:
chardet = None
try:
from urllib3.contrib import pyopenssl
except ImportError:
pyopenssl = None
OpenSSL = None
cryptography = None
else:
import cryptography # type: ignore[import-not-found]
import OpenSSL # type: ignore[import-not-found]
def _implementation():
"""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 3.10.3 it will return
{'name': 'CPython', 'version': '3.10.3'}.
This function works best on CPython and PyPy: in particular, it probably
doesn't work for Jython or IronPython. Future investigation should be done
to work out the correct shape of the code for those platforms.
"""
implementation = platform.python_implementation()
if implementation == "CPython":
implementation_version = platform.python_version()
elif implementation == "PyPy":
pypy = sys.pypy_version_info # type: ignore[attr-defined]
implementation_version = f"{pypy.major}.{pypy.minor}.{pypy.micro}"
if sys.pypy_version_info.releaselevel != "final": # type: ignore[attr-defined]
implementation_version = "".join(
[implementation_version, sys.pypy_version_info.releaselevel] # type: ignore[attr-defined]
)
elif implementation == "Jython":
implementation_version = platform.python_version() # Complete Guess
elif implementation == "IronPython":
implementation_version = platform.python_version() # Complete Guess
else:
implementation_version = "Unknown"
return {"name": implementation, "version": implementation_version}
def info() -> dict[str, Any]:
"""Generate information for a bug report."""
try:
platform_info = {
"system": platform.system(),
"release": platform.release(),
}
except OSError:
platform_info = {
"system": "Unknown",
"release": "Unknown",
}
implementation_info = _implementation()
urllib3_info = {"version": urllib3.__version__} # type: ignore[reportPrivateImportUsage]
charset_normalizer_info = {"version": None}
chardet_info: dict[str, str | None] = {"version": None}
if charset_normalizer:
charset_normalizer_info = {"version": charset_normalizer.__version__}
if chardet:
chardet_info = {"version": chardet.__version__}
pyopenssl_info: dict[str, str | None] = {
"version": None,
"openssl_version": "",
}
if OpenSSL:
pyopenssl_info = {
"version": OpenSSL.__version__,
"openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}",
}
cryptography_info = {
"version": getattr(cryptography, "__version__", ""),
}
idna_info = {
"version": getattr(idna, "__version__", ""),
}
system_ssl = ssl.OPENSSL_VERSION_NUMBER
system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} # type: ignore[reportUnnecessaryComparison]
return {
"platform": platform_info,
"implementation": implementation_info,
"system_ssl": system_ssl_info,
"using_pyopenssl": pyopenssl is not None,
"using_charset_normalizer": chardet is None,
"pyOpenSSL": pyopenssl_info,
"urllib3": urllib3_info,
"chardet": chardet_info,
"charset_normalizer": charset_normalizer_info,
"cryptography": cryptography_info,
"idna": idna_info,
"requests": {
"version": requests_version,
},
}
def main():
"""Pretty-print the bug information as JSON."""
print(json.dumps(info(), sort_keys=True, indent=2))
if __name__ == "__main__":
main()

48
src/requests/hooks.py Normal file
View File

@@ -0,0 +1,48 @@
"""
requests.hooks
~~~~~~~~~~~~~~
This module provides the capabilities for the Requests hooks system.
Available hooks:
``response``:
The response generated from a Request.
"""
from __future__ import annotations
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from . import _types as _t
from .models import Response
HOOKS: list[str] = ["response"]
def default_hooks() -> dict[str, list[_t.HookType]]:
return {event: [] for event in HOOKS}
# TODO: response is the only one
def dispatch_hook(
key: str,
hooks: _t.HooksInputType | None,
hook_data: Response,
**kwargs: Any,
) -> Response:
"""Dispatches a hook dictionary on a given piece of data."""
hooks_dict = hooks or {}
hook_list: Iterable[_t.HookType] | _t.HookType | None = hooks_dict.get(key)
if hook_list:
if isinstance(hook_list, Callable):
hook_list = [hook_list]
for hook in hook_list:
_hook_data = hook(hook_data, **kwargs)
if _hook_data is not None:
hook_data = _hook_data
return hook_data

File diff suppressed because it is too large Load Diff

23
src/requests/packages.py Normal file
View File

@@ -0,0 +1,23 @@
import sys
from .compat import chardet
# This code exists for backwards compatibility reasons.
# I don't like it either. Just look the other way. :)
for package in ("urllib3", "idna"):
locals()[package] = __import__(package)
# This traversal is apparently necessary such that the identities are
# preserved (requests.packages.urllib3.* is urllib3.*)
for mod in list(sys.modules):
if mod == package or mod.startswith(f"{package}."):
sys.modules[f"requests.packages.{mod}"] = sys.modules[mod]
if chardet is not None:
target = chardet.__name__
for mod in list(sys.modules):
if mod == target or mod.startswith(f"{target}."):
imported_mod = sys.modules[mod]
sys.modules[f"requests.packages.{mod}"] = imported_mod
mod = mod.replace(target, "chardet")
sys.modules[f"requests.packages.{mod}"] = imported_mod

0
src/requests/py.typed Normal file
View File

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
requests.sessions
~~~~~~~~~~~~~~~~~
@@ -7,47 +5,77 @@ requests.sessions
This module provides a Session object to manage and persist settings across
requests (cookies, auth, proxies).
"""
from __future__ import annotations
import os
import sys
import time
from datetime import timedelta
from collections import OrderedDict
from collections.abc import Generator, Mapping, MutableMapping
from datetime import timedelta
from typing import TYPE_CHECKING, Any, cast
from .auth import _basic_auth_str
from .compat import cookielib, is_py3, urljoin, urlparse, Mapping
from .cookies import (
cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT
from .hooks import default_hooks, dispatch_hook
from ._internal_utils import to_native_string
from .utils import to_key_val_list, default_headers, DEFAULT_PORTS
from .exceptions import (
TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError)
from .structures import CaseInsensitiveDict
from ._types import is_prepared as _is_prepared
from .adapters import HTTPAdapter
from .utils import (
requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies,
get_auth_from_url, rewind_body, resolve_proxies
from .auth import _basic_auth_str # type: ignore[reportPrivateUsage]
from .compat import cookielib, urljoin, urlparse
from .cookies import (
RequestsCookieJar,
cookiejar_from_dict,
extract_cookies_to_jar,
merge_cookies,
)
from .status_codes import codes
from .exceptions import (
ChunkedEncodingError,
ContentDecodingError,
InvalidSchema,
TooManyRedirects,
)
from .hooks import default_hooks, dispatch_hook
# formerly defined here, reexposed here for backward compatibility
from .models import REDIRECT_STATI
from .models import ( # noqa: F401
DEFAULT_REDIRECT_LIMIT,
REDIRECT_STATI, # type: ignore[reportUnusedImport]
PreparedRequest,
Request,
Response,
)
from .status_codes import codes
from .structures import CaseInsensitiveDict
from .utils import ( # noqa: F401
DEFAULT_PORTS,
default_headers,
get_auth_from_url,
get_environ_proxies,
get_netrc_auth,
requote_uri,
resolve_proxies,
rewind_body,
should_bypass_proxies, # type: ignore[reportUnusedImport] # re-export for external consumers
to_key_val_list,
)
if TYPE_CHECKING:
from http.cookiejar import CookieJar
from typing_extensions import Self, Unpack
from . import _types as _t
from .adapters import BaseAdapter
# Preferred clock, based on which one is more accurate on a given system.
if sys.platform == 'win32':
try: # Python 3.4+
preferred_clock = time.perf_counter
except AttributeError: # Earlier than Python 3.
preferred_clock = time.clock
if sys.platform == "win32":
preferred_clock = time.perf_counter
else:
preferred_clock = time.time
def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
def merge_setting(
request_setting: Any, session_setting: Any, dict_class: type = OrderedDict
) -> Any:
"""Determines appropriate setting for a given request, taking into account
the explicit setting on that request, and the setting in the session. If a
setting is a dictionary, they will be merged together using `dict_class`
@@ -61,13 +89,12 @@ def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
# Bypass if not a dictionary (e.g. verify)
if not (
isinstance(session_setting, Mapping) and
isinstance(request_setting, Mapping)
isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)
):
return request_setting
merged_setting = dict_class(to_key_val_list(session_setting))
merged_setting.update(to_key_val_list(request_setting))
merged_setting = dict_class(to_key_val_list(session_setting)) # type: ignore[arg-type] # isinstance narrows Any to Mapping[Unknown]
merged_setting.update(to_key_val_list(request_setting)) # type: ignore[arg-type]
# Remove keys that are set to None. Extract keys first to avoid altering
# the dictionary during iteration.
@@ -78,24 +105,33 @@ def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
return merged_setting
def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
def merge_hooks(
request_hooks: _t.HooksType,
session_hooks: _t.HooksType,
dict_class: type = OrderedDict,
) -> _t.HooksType:
"""Properly merges both requests and session hooks.
This is necessary because when request_hooks == {'response': []}, the
merge breaks Session hooks entirely.
"""
if session_hooks is None or session_hooks.get('response') == []:
if session_hooks is None or session_hooks.get("response") == []:
return request_hooks
if request_hooks is None or request_hooks.get('response') == []:
if request_hooks is None or request_hooks.get("response") == []:
return session_hooks
return merge_setting(request_hooks, session_hooks, dict_class)
class SessionRedirectMixin(object):
class SessionRedirectMixin:
max_redirects: int
trust_env: bool
cookies: RequestsCookieJar
def get_redirect_target(self, resp):
def send(self, request: PreparedRequest, **kwargs: Any) -> Response: ...
def get_redirect_target(self, resp: Response) -> str | None:
"""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
@@ -104,19 +140,18 @@ class SessionRedirectMixin(object):
# to cache the redirect location onto the response object as a private
# attribute.
if resp.is_redirect:
location = resp.headers['location']
location = resp.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.
# It is more likely to get UTF8 header rather than latin1.
# This causes incorrect handling of UTF8 encoded location headers.
# To solve this, we re-encode the location in latin1.
if is_py3:
location = location.encode('latin1')
return to_native_string(location, 'utf8')
location = location.encode("latin1")
return to_native_string(location, "utf8")
return None
def should_strip_auth(self, old_url, new_url):
def should_strip_auth(self, old_url: str, new_url: str) -> bool:
"""Decide whether Authorization header should be removed when redirecting"""
old_parsed = urlparse(old_url)
new_parsed = urlparse(new_url)
@@ -126,26 +161,43 @@ class SessionRedirectMixin(object):
# ports. This isn't specified by RFC 7235, but is kept to avoid
# breaking backwards compatibility with older versions of requests
# that allowed any redirects on the same host.
if (old_parsed.scheme == 'http' and old_parsed.port in (80, None)
and new_parsed.scheme == 'https' and new_parsed.port in (443, None)):
if (
old_parsed.scheme == "http"
and old_parsed.port in (80, None)
and new_parsed.scheme == "https"
and new_parsed.port in (443, None)
):
return False
# Handle default port usage corresponding to scheme.
changed_port = old_parsed.port != new_parsed.port
changed_scheme = old_parsed.scheme != new_parsed.scheme
default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
if (not changed_scheme and old_parsed.port in default_port
and new_parsed.port in default_port):
if (
not changed_scheme
and old_parsed.port in default_port
and new_parsed.port in default_port
):
return False
# Standard case: root URI must match
return changed_port or changed_scheme
def resolve_redirects(self, resp, req, stream=False, timeout=None,
verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs):
def resolve_redirects(
self,
resp: Response,
req: PreparedRequest,
stream: bool = False,
timeout: _t.TimeoutType = None,
verify: _t.VerifyType = True,
cert: _t.CertType = None,
proxies: dict[str, str] | None = None,
yield_requests: bool = False,
**adapter_kwargs: Any,
) -> Generator[Response, None, None]:
"""Receives a Response. Returns a generator of Responses or Requests."""
hist = [] # keep track of history
hist: list[Response] = [] # keep track of history
url = self.get_redirect_target(resp)
previous_fragment = urlparse(req.url).fragment
@@ -153,9 +205,8 @@ class SessionRedirectMixin(object):
prepared_request = req.copy()
# Update history and keep track of redirects.
# resp.history must ignore the original request in this loop
resp.history = hist[:]
hist.append(resp)
resp.history = hist[1:]
try:
resp.content # Consume socket so it can be released
@@ -163,19 +214,21 @@ class SessionRedirectMixin(object):
resp.raw.read(decode_content=False)
if len(resp.history) >= self.max_redirects:
raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
raise TooManyRedirects(
f"Exceeded {self.max_redirects} redirects.", response=resp
)
# Release the connection back into the pool.
resp.close()
# Handle redirection without scheme (see: RFC 1808 Section 4)
if url.startswith('//'):
if url.startswith("//"):
parsed_rurl = urlparse(resp.url)
url = ':'.join([to_native_string(parsed_rurl.scheme), url])
url = ":".join([to_native_string(parsed_rurl.scheme), url])
# Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
parsed = urlparse(url)
if parsed.fragment == '' and previous_fragment:
if parsed.fragment == "" and previous_fragment:
parsed = parsed._replace(fragment=previous_fragment)
elif parsed.fragment:
previous_fragment = parsed.fragment
@@ -194,22 +247,26 @@ class SessionRedirectMixin(object):
self.rebuild_method(prepared_request, resp)
# https://github.com/psf/requests/issues/1084
if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect):
if resp.status_code not in (
codes.temporary_redirect,
codes.permanent_redirect,
):
# https://github.com/psf/requests/issues/3490
purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding')
purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")
for header in purged_headers:
prepared_request.headers.pop(header, None)
prepared_request.body = None
headers = prepared_request.headers
headers.pop('Cookie', None)
headers.pop("Cookie", None)
# Extract any cookies sent on the response to the cookiejar
# in the new request. Because we've mutated our copied prepared
# request, use the old one that we haven't yet touched.
extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
merge_cookies(prepared_request._cookies, self.cookies)
prepared_request.prepare_cookies(prepared_request._cookies)
cookie_jar = cast("CookieJar", prepared_request._cookies) # type: ignore[reportPrivateUsage]
extract_cookies_to_jar(cookie_jar, req, resp.raw)
merge_cookies(cookie_jar, self.cookies)
prepared_request.prepare_cookies(cookie_jar)
# Rebuild auth and proxy information.
proxies = self.rebuild_proxies(prepared_request, proxies)
@@ -218,9 +275,8 @@ class SessionRedirectMixin(object):
# A failed tell() sets `_body_position` to `object()`. This non-None
# value ensures `rewindable` will be True, allowing us to raise an
# UnrewindableBodyError, instead of hanging the connection.
rewindable = (
prepared_request._body_position is not None and
('Content-Length' in headers or 'Transfer-Encoding' in headers)
rewindable = prepared_request._body_position is not None and ( # type: ignore[reportPrivateUsage]
"Content-Length" in headers or "Transfer-Encoding" in headers
)
# Attempt to rewind consumed file-like object.
@@ -231,9 +287,8 @@ class SessionRedirectMixin(object):
req = prepared_request
if yield_requests:
yield req
yield req # type: ignore[misc] # Internal use only, returns PreparedRequest
else:
resp = self.send(
req,
stream=stream,
@@ -242,7 +297,7 @@ class SessionRedirectMixin(object):
cert=cert,
proxies=proxies,
allow_redirects=False,
**adapter_kwargs
**adapter_kwargs,
)
extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
@@ -251,25 +306,36 @@ class SessionRedirectMixin(object):
url = self.get_redirect_target(resp)
yield resp
def rebuild_auth(self, prepared_request, response):
def rebuild_auth(
self, prepared_request: PreparedRequest, response: Response
) -> None:
"""When being redirected we may want to strip authentication from the
request to avoid leaking credentials. This method intelligently removes
and reapplies authentication where possible to avoid credential loss.
"""
original_request = response.request
assert _is_prepared(original_request)
assert _is_prepared(prepared_request)
headers = prepared_request.headers
original_url = original_request.url
url = prepared_request.url
if 'Authorization' in headers and self.should_strip_auth(response.request.url, url):
if "Authorization" in headers and self.should_strip_auth(original_url, url):
# If we get redirected to a new host, we should strip out any
# authentication headers.
del headers['Authorization']
del headers["Authorization"]
# .netrc might have more auth for us on our new host.
new_auth = get_netrc_auth(url) if self.trust_env else None
if new_auth is not None:
prepared_request.prepare_auth(new_auth)
def rebuild_proxies(self, prepared_request, proxies):
def rebuild_proxies(
self,
prepared_request: PreparedRequest,
proxies: dict[str, str] | None,
) -> dict[str, str]:
"""This method re-evaluates the proxy configuration by considering the
environment variables. If we are redirected to a URL covered by
NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
@@ -281,42 +347,47 @@ class SessionRedirectMixin(object):
:rtype: dict
"""
assert _is_prepared(prepared_request)
headers = prepared_request.headers
scheme = urlparse(prepared_request.url).scheme
new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
if 'Proxy-Authorization' in headers:
del headers['Proxy-Authorization']
if "Proxy-Authorization" in headers:
del headers["Proxy-Authorization"]
try:
username, password = get_auth_from_url(new_proxies[scheme])
except KeyError:
username, password = None, None
if username and password:
headers['Proxy-Authorization'] = _basic_auth_str(username, password)
# urllib3 handles proxy authorization for us in the standard adapter.
# Avoid appending this to TLS tunneled requests where it may be leaked.
if not scheme.startswith("https") and username and password:
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
return new_proxies
def rebuild_method(self, prepared_request, response):
def rebuild_method(
self, prepared_request: PreparedRequest, response: Response
) -> None:
"""When being redirected we may want to change the method of the request
based on certain specs or browser behavior.
"""
method = prepared_request.method
# https://tools.ietf.org/html/rfc7231#section-6.4.4
if response.status_code == codes.see_other and method != 'HEAD':
method = 'GET'
if response.status_code == codes.see_other and method != "HEAD":
method = "GET"
# Do what the browsers do, despite standards...
# First, turn 302s into GETs.
if response.status_code == codes.found and method != 'HEAD':
method = 'GET'
if response.status_code == codes.found and method != "HEAD":
method = "GET"
# Second, if a POST is responded to with a 301, turn it into a GET.
# This bizarre behaviour is explained in Issue 1704.
if response.status_code == codes.moved and method == 'POST':
method = 'GET'
if response.status_code == codes.moved and method == "POST":
method = "GET"
prepared_request.method = method
@@ -340,38 +411,46 @@ class Session(SessionRedirectMixin):
<Response [200]>
"""
__attrs__ = [
'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify',
'cert', 'adapters', 'stream', 'trust_env',
'max_redirects',
__attrs__: list[str] = [
"headers",
"cookies",
"auth",
"proxies",
"hooks",
"params",
"verify",
"cert",
"adapters",
"stream",
"trust_env",
"max_redirects",
]
def __init__(self):
def __init__(self) -> None:
#: A case-insensitive dictionary of headers to be sent on each
#: :class:`Request <Request>` sent from this
#: :class:`Session <Session>`.
self.headers = default_headers()
self.headers: CaseInsensitiveDict[str] = default_headers()
#: Default Authentication tuple or object to attach to
#: :class:`Request <Request>`.
self.auth = None
self.auth: _t.AuthType = None
#: Dictionary mapping protocol or protocol and host to the URL of the proxy
#: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
#: be used on each :class:`Request <Request>`.
self.proxies = {}
self.proxies: dict[str, str] = {}
#: Event-handling hooks.
self.hooks = default_hooks()
self.hooks: dict[str, list[_t.HookType]] = default_hooks()
#: Dictionary of querystring data to attach to each
#: :class:`Request <Request>`. The dictionary values may be lists for
#: representing multivalued query parameters.
self.params = {}
self.params: MutableMapping[str, Any] = {}
#: Stream response content default.
self.stream = False
self.stream: bool = False
#: SSL Verification default.
#: Defaults to `True`, requiring requests to verify the TLS certificate at the
@@ -381,40 +460,42 @@ class Session(SessionRedirectMixin):
#: expired certificates, which will make your application vulnerable to
#: man-in-the-middle (MitM) attacks.
#: Only set this to `False` for testing.
self.verify = True
#: If verify is set to a string, it must be the path to a CA bundle file
#: that will be used to verify the TLS certificate.
self.verify: _t.VerifyType = True
#: SSL client certificate default, if String, path to ssl client
#: cert file (.pem). If Tuple, ('cert', 'key') pair.
self.cert = None
self.cert: _t.CertType = None
#: Maximum number of redirects allowed. If the request exceeds this
#: limit, a :class:`TooManyRedirects` exception is raised.
#: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
#: 30.
self.max_redirects = DEFAULT_REDIRECT_LIMIT
self.max_redirects: int = DEFAULT_REDIRECT_LIMIT
#: Trust environment settings for proxy configuration, default
#: authentication and similar.
self.trust_env = True
self.trust_env: bool = True
#: A CookieJar containing all currently outstanding cookies set on this
#: session. By default it is a
#: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
#: may be any other ``cookielib.CookieJar`` compatible object.
self.cookies = cookiejar_from_dict({})
self.cookies: RequestsCookieJar = cookiejar_from_dict({})
# Default connection adapters.
self.adapters = OrderedDict()
self.mount('https://', HTTPAdapter())
self.mount('http://', HTTPAdapter())
self.adapters: MutableMapping[str, BaseAdapter] = OrderedDict()
self.mount("https://", HTTPAdapter())
self.mount("http://", HTTPAdapter())
def __enter__(self):
def __enter__(self) -> Self:
return self
def __exit__(self, *args):
def __exit__(self, *args: Any) -> None:
self.close()
def prepare_request(self, request):
def prepare_request(self, request: Request) -> PreparedRequest:
"""Constructs a :class:`PreparedRequest <PreparedRequest>` for
transmission and returns it. The :class:`PreparedRequest` has settings
merged from the :class:`Request <Request>` instance and those of the
@@ -424,6 +505,9 @@ class Session(SessionRedirectMixin):
session's settings.
:rtype: requests.PreparedRequest
"""
url = cast("_t.UriType", request.url)
method = cast(str, request.method)
cookies = request.cookies or {}
# Bootstrap CookieJar.
@@ -432,21 +516,24 @@ class Session(SessionRedirectMixin):
# Merge with session cookies
merged_cookies = merge_cookies(
merge_cookies(RequestsCookieJar(), self.cookies), cookies)
merge_cookies(RequestsCookieJar(), self.cookies), cookies
)
# Set environment's basic authentication if not explicitly set.
auth = request.auth
if self.trust_env and not auth and not self.auth:
auth = get_netrc_auth(request.url)
auth = get_netrc_auth(url)
p = PreparedRequest()
p.prepare(
method=request.method.upper(),
url=request.url,
method=method.upper(),
url=url,
files=request.files,
data=request.data,
json=request.json,
headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict),
headers=merge_setting(
request.headers, self.headers, dict_class=CaseInsensitiveDict
),
params=merge_setting(request.params, self.params),
auth=merge_setting(auth, self.auth),
cookies=merged_cookies,
@@ -454,10 +541,25 @@ class Session(SessionRedirectMixin):
)
return p
def request(self, method, url,
params=None, data=None, headers=None, cookies=None, files=None,
auth=None, timeout=None, allow_redirects=True, proxies=None,
hooks=None, stream=None, verify=None, cert=None, json=None):
def request(
self,
method: str,
url: _t.UriType,
params: _t.ParamsType = None,
data: _t.DataType = None,
headers: Mapping[str, str | bytes] | None = None,
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,
files: _t.FilesType = None,
auth: _t.AuthType = None,
timeout: _t.TimeoutType = None,
allow_redirects: bool = True,
proxies: dict[str, str] | None = None,
hooks: _t.HooksType = None,
stream: bool | None = None,
verify: _t.VerifyType | None = None,
cert: _t.CertType = None,
json: _t.JsonType = None,
) -> Response:
"""Constructs a :class:`Request <Request>`, prepares it and sends it.
Returns :class:`Response <Response>` object.
@@ -477,7 +579,7 @@ class Session(SessionRedirectMixin):
for multipart encoding upload.
:param auth: (optional) Auth tuple or callable to enable
Basic/Digest/Custom HTTP Auth.
:param timeout: (optional) How long to wait for the server to send
:param timeout: (optional) How many seconds to wait for the server to send
data before giving up, as a float, or a :ref:`(connect timeout,
read timeout) <timeouts>` tuple.
:type timeout: float or tuple
@@ -485,6 +587,8 @@ class Session(SessionRedirectMixin):
:type allow_redirects: bool
:param proxies: (optional) Dictionary mapping protocol or protocol and
hostname to the URL of the proxy.
:param hooks: (optional) Dictionary mapping hook name to one event or
list of events, event must be callable.
:param stream: (optional) whether to immediately download the response
content. Defaults to ``False``.
:param verify: (optional) Either a boolean, in which case it controls whether we verify
@@ -493,12 +597,15 @@ class Session(SessionRedirectMixin):
``False``, requests will accept any TLS certificate presented by
the server, and will ignore hostname mismatches and/or expired
certificates, which will make your application vulnerable to
man-in-the-middle (MitM) attacks. Setting verify to ``False``
man-in-the-middle (MitM) attacks. Setting verify to ``False``
may be useful during local development or testing.
:param cert: (optional) if String, path to ssl client cert file (.pem).
If Tuple, ('cert', 'key') pair.
:rtype: requests.Response
"""
if isinstance(url, bytes):
url = url.decode("utf-8")
# Create the Request.
req = Request(
method=method.upper(),
@@ -514,6 +621,8 @@ class Session(SessionRedirectMixin):
)
prep = self.prepare_request(req)
assert _is_prepared(prep)
proxies = proxies or {}
settings = self.merge_environment_settings(
@@ -522,15 +631,15 @@ class Session(SessionRedirectMixin):
# Send the request.
send_kwargs = {
'timeout': timeout,
'allow_redirects': allow_redirects,
"timeout": timeout,
"allow_redirects": allow_redirects,
}
send_kwargs.update(settings)
resp = self.send(prep, **send_kwargs)
return resp
def get(self, url, **kwargs):
def get(self, url: _t.UriType, **kwargs: Unpack[_t.GetKwargs]) -> Response:
r"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -538,10 +647,10 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return self.request('GET', url, **kwargs)
kwargs.setdefault("allow_redirects", True)
return self.request("GET", url, **kwargs)
def options(self, url, **kwargs):
def options(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends a OPTIONS request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -549,10 +658,10 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return self.request('OPTIONS', url, **kwargs)
kwargs.setdefault("allow_redirects", True)
return self.request("OPTIONS", url, **kwargs)
def head(self, url, **kwargs):
def head(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends a HEAD request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -560,10 +669,16 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', False)
return self.request('HEAD', url, **kwargs)
kwargs.setdefault("allow_redirects", False)
return self.request("HEAD", url, **kwargs)
def post(self, url, data=None, json=None, **kwargs):
def post(
self,
url: _t.UriType,
data: _t.DataType = None,
json: _t.JsonType = None,
**kwargs: Unpack[_t.PostKwargs],
) -> Response:
r"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -574,9 +689,11 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
return self.request('POST', url, data=data, json=json, **kwargs)
return self.request("POST", url, data=data, json=json, **kwargs)
def put(self, url, data=None, **kwargs):
def put(
self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]
) -> Response:
r"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -586,9 +703,11 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
return self.request('PUT', url, data=data, **kwargs)
return self.request("PUT", url, data=data, **kwargs)
def patch(self, url, data=None, **kwargs):
def patch(
self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]
) -> Response:
r"""Sends a PATCH request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -598,9 +717,9 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
return self.request('PATCH', url, data=data, **kwargs)
return self.request("PATCH", url, data=data, **kwargs)
def delete(self, url, **kwargs):
def delete(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
r"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
@@ -608,31 +727,31 @@ class Session(SessionRedirectMixin):
:rtype: requests.Response
"""
return self.request('DELETE', url, **kwargs)
return self.request("DELETE", url, **kwargs)
def send(self, request, **kwargs):
def send(self, request: PreparedRequest, **kwargs: Any) -> Response:
"""Send a given PreparedRequest.
:rtype: requests.Response
"""
# 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)
if 'proxies' not in kwargs:
kwargs['proxies'] = resolve_proxies(
request, self.proxies, self.trust_env
)
kwargs.setdefault("stream", self.stream)
kwargs.setdefault("verify", self.verify)
kwargs.setdefault("cert", self.cert)
if "proxies" not in kwargs:
kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
# It's possible that users might accidentally send a Request object.
# Guard against that specific failure case.
if isinstance(request, Request):
raise ValueError('You can only send PreparedRequests.')
raise ValueError("You can only send PreparedRequests.")
assert _is_prepared(request)
# Set up variables needed for resolve_redirects and dispatching of hooks
allow_redirects = kwargs.pop('allow_redirects', True)
stream = kwargs.get('stream')
allow_redirects = kwargs.pop("allow_redirects", True)
stream = kwargs.get("stream")
hooks = request.hooks
# Get the appropriate adapter to use
@@ -649,11 +768,10 @@ class Session(SessionRedirectMixin):
r.elapsed = timedelta(seconds=elapsed)
# Response manipulation hooks
r = dispatch_hook('response', hooks, r, **kwargs)
r = dispatch_hook("response", hooks, r, **kwargs)
# Persist cookies
if r.history:
# If the hooks create history then we want those cookies too
for resp in r.history:
extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
@@ -679,7 +797,9 @@ class Session(SessionRedirectMixin):
# If redirects aren't being followed, store the response on the Request for Response.next().
if not allow_redirects:
try:
r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs))
r._next = next( # type: ignore[assignment] # yield_requests=True returns PreparedRequest
self.resolve_redirects(r, request, yield_requests=True, **kwargs)
)
except StopIteration:
pass
@@ -688,7 +808,14 @@ class Session(SessionRedirectMixin):
return r
def merge_environment_settings(self, url, proxies, stream, verify, cert):
def merge_environment_settings(
self,
url: str,
proxies: dict[str, str] | None,
stream: bool | None,
verify: _t.VerifyType | None,
cert: _t.CertType,
) -> dict[str, Any]:
"""
Check the environment and merge it with some settings.
@@ -697,16 +824,20 @@ class Session(SessionRedirectMixin):
# Gather clues from the surrounding environment.
if self.trust_env:
# Set environment's proxies.
no_proxy = proxies.get('no_proxy') if proxies is not None else None
no_proxy = proxies.get("no_proxy") if proxies is not None else None
env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
for (k, v) in env_proxies.items():
proxies.setdefault(k, v)
if proxies is not None:
for k, v in env_proxies.items():
proxies.setdefault(k, v)
# Look for requests environment configuration and be compatible
# with cURL.
# Look for requests environment configuration
# and be compatible with cURL.
if verify is True or verify is None:
verify = (os.environ.get('REQUESTS_CA_BUNDLE') or
os.environ.get('CURL_CA_BUNDLE'))
verify = (
os.environ.get("REQUESTS_CA_BUNDLE")
or os.environ.get("CURL_CA_BUNDLE")
or verify
)
# Merge all the kwargs.
proxies = merge_setting(proxies, self.proxies)
@@ -714,29 +845,27 @@ class Session(SessionRedirectMixin):
verify = merge_setting(verify, self.verify)
cert = merge_setting(cert, self.cert)
return {'verify': verify, 'proxies': proxies, 'stream': stream,
'cert': cert}
return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
def get_adapter(self, url):
def get_adapter(self, url: str) -> BaseAdapter:
"""
Returns the appropriate connection adapter for the given URL.
:rtype: requests.adapters.BaseAdapter
"""
for (prefix, adapter) in self.adapters.items():
for prefix, adapter in self.adapters.items():
if url.lower().startswith(prefix.lower()):
return adapter
# Nothing matches :-/
raise InvalidSchema("No connection adapters were found for {!r}".format(url))
raise InvalidSchema(f"No connection adapters were found for {url!r}")
def close(self):
def close(self) -> None:
"""Closes all adapters and as such the session"""
for v in self.adapters.values():
v.close()
def mount(self, prefix, adapter):
def mount(self, prefix: str, adapter: BaseAdapter) -> None:
"""Registers a connection adapter to a prefix.
Adapters are sorted in descending order by prefix length.
@@ -747,16 +876,16 @@ class Session(SessionRedirectMixin):
for key in keys_to_move:
self.adapters[key] = self.adapters.pop(key)
def __getstate__(self):
def __getstate__(self) -> dict[str, Any]:
state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
return state
def __setstate__(self, state):
def __setstate__(self, state: dict[str, Any]) -> None:
for attr, value in state.items():
setattr(self, attr, value)
def session():
def session() -> Session:
"""
Returns a :class:`Session` for context-management.

View File

@@ -0,0 +1,128 @@
r"""
The ``codes`` object defines a mapping from common names for HTTP statuses
to their numerical codes, accessible either as attributes or as dictionary
items.
Example::
>>> import requests
>>> requests.codes['temporary_redirect']
307
>>> requests.codes.teapot
418
>>> requests.codes['\o/']
200
Some codes have multiple names, and both upper- and lower-case versions of
the names are allowed. For example, ``codes.ok``, ``codes.OK``, and
``codes.okay`` all correspond to the HTTP status code 200.
"""
from .structures import LookupDict
_codes = {
# Informational.
100: ("continue",),
101: ("switching_protocols",),
102: ("processing", "early-hints"),
103: ("checkpoint",),
122: ("uri_too_long", "request_uri_too_long"),
200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", ""),
201: ("created",),
202: ("accepted",),
203: ("non_authoritative_info", "non_authoritative_information"),
204: ("no_content",),
205: ("reset_content", "reset"),
206: ("partial_content", "partial"),
207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"),
208: ("already_reported",),
226: ("im_used",),
# Redirection.
300: ("multiple_choices",),
301: ("moved_permanently", "moved", "\\o-"),
302: ("found",),
303: ("see_other", "other"),
304: ("not_modified",),
305: ("use_proxy",),
306: ("switch_proxy",),
307: ("temporary_redirect", "temporary_moved", "temporary"),
308: (
"permanent_redirect",
"resume_incomplete",
"resume",
), # "resume" and "resume_incomplete" to be removed in 3.0
# Client Error.
400: ("bad_request", "bad"),
401: ("unauthorized",),
402: ("payment_required", "payment"),
403: ("forbidden",),
404: ("not_found", "-o-"),
405: ("method_not_allowed", "not_allowed"),
406: ("not_acceptable",),
407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"),
408: ("request_timeout", "timeout"),
409: ("conflict",),
410: ("gone",),
411: ("length_required",),
412: ("precondition_failed", "precondition"),
413: ("request_entity_too_large", "content_too_large"),
414: ("request_uri_too_large", "uri_too_long"),
415: ("unsupported_media_type", "unsupported_media", "media_type"),
416: (
"requested_range_not_satisfiable",
"requested_range",
"range_not_satisfiable",
),
417: ("expectation_failed",),
418: ("im_a_teapot", "teapot", "i_am_a_teapot"),
421: ("misdirected_request",),
422: ("unprocessable_entity", "unprocessable", "unprocessable_content"),
423: ("locked",),
424: ("failed_dependency", "dependency"),
425: ("unordered_collection", "unordered", "too_early"),
426: ("upgrade_required", "upgrade"),
428: ("precondition_required", "precondition"),
429: ("too_many_requests", "too_many"),
431: ("header_fields_too_large", "fields_too_large"),
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.
500: ("internal_server_error", "server_error", "/o\\", ""),
501: ("not_implemented",),
502: ("bad_gateway",),
503: ("service_unavailable", "unavailable"),
504: ("gateway_timeout",),
505: ("http_version_not_supported", "http_version"),
506: ("variant_also_negotiates",),
507: ("insufficient_storage",),
509: ("bandwidth_limit_exceeded", "bandwidth"),
510: ("not_extended",),
511: ("network_authentication_required", "network_auth", "network_authentication"),
}
codes: LookupDict[int] = LookupDict(name="status_codes")
def _init():
for code, titles in _codes.items():
for title in titles:
setattr(codes, title, code)
if not title.startswith(("\\", "/")):
setattr(codes, title.upper(), code)
def doc(code: int) -> str:
names = ", ".join(f"``{n}``" for n in _codes[code])
return "* %d: %s" % (code, names)
global __doc__
__doc__ = (
__doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes))
if __doc__ is not None
else None
)
_init()

126
src/requests/structures.py Normal file
View File

@@ -0,0 +1,126 @@
"""
requests.structures
~~~~~~~~~~~~~~~~~~~
Data structures that power Requests.
"""
from __future__ import annotations
from collections import OrderedDict
from collections.abc import Iterable, Iterator, Mapping
from typing import Any, Generic, TypeVar, overload
from .compat import MutableMapping
_VT = TypeVar("_VT")
_D = TypeVar("_D")
class CaseInsensitiveDict(MutableMapping[str, _VT], Generic[_VT]):
"""A case-insensitive ``dict``-like object.
Implements all methods and operations of
``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, regardless
of how the header name was originally stored.
If the constructor, ``.update``, or equality comparison
operations are given keys that have equal ``.lower()``s, the
behavior is undefined.
"""
def __init__(
self,
data: Mapping[str, _VT] | Iterable[tuple[str, _VT]] | None = None,
**kwargs: _VT,
) -> None:
self._store: OrderedDict[str, tuple[str, _VT]] = OrderedDict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key: str, value: _VT) -> None:
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
def __getitem__(self, key: str) -> _VT:
return self._store[key.lower()][1]
def __delitem__(self, key: str) -> None:
del self._store[key.lower()]
def __iter__(self) -> Iterator[str]:
return (casedkey for casedkey, _ in self._store.values())
def __len__(self) -> int:
return len(self._store)
def lower_items(self) -> Iterator[tuple[str, _VT]]:
"""Like iteritems(), but with all lowercase keys."""
return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items())
def __eq__(self, other: object) -> bool:
if isinstance(other, Mapping):
other_dict: CaseInsensitiveDict[Any] = CaseInsensitiveDict(other) # type: ignore[reportUnknownArgumentType]
else:
return NotImplemented
# Compare insensitively
return dict(self.lower_items()) == dict(other_dict.lower_items())
# Copy is required
def copy(self) -> CaseInsensitiveDict[_VT]:
return CaseInsensitiveDict(self._store.values())
def __repr__(self) -> str:
return str(dict(self.items()))
class LookupDict(dict[str, _VT]):
"""Dictionary lookup object."""
def __init__(self, name: Any = None) -> None:
self.name: bytes | str = name
super().__init__()
def __repr__(self) -> str:
return f"<lookup '{self.name}'>"
def __getattr__(self, key: str) -> _VT | None:
# We need this for type checkers to infer typing
# on attribute access with status_codes.py
if key in self.__dict__:
return self.__dict__[key]
else:
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{key}'"
)
def __getitem__(self, key: str) -> _VT | None: # type: ignore[override]
# We allow fall-through here, so values default to None
return self.__dict__.get(key, None)
@overload
def get(self, key: str, default: None = None) -> _VT | None: ...
@overload
def get(self, key: str, default: _D | _VT) -> _D | _VT: ...
def get(self, key: str, default: _D | None = None) -> _VT | _D | None:
return self.__dict__.get(key, default)

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
"""Requests test package initialisation."""
import warnings
import urllib3
from urllib3.exceptions import SNIMissingWarning
try:
from urllib3.exceptions import SNIMissingWarning
# urllib3 sets SNIMissingWarning to only go off once,
# while this test suite requires it to always fire
# so that it occurs during test_requests.test_https_warnings
warnings.simplefilter('always', SNIMissingWarning)
# urllib3 1.x sets SNIMissingWarning to only go off once,
# while this test suite requires it to always fire
# so that it occurs during test_requests.test_https_warnings
warnings.simplefilter("always", SNIMissingWarning)
except ImportError:
# urllib3 2.0 removed that warning and errors out instead
SNIMissingWarning = None

10
tests/certs/README.md Normal file
View File

@@ -0,0 +1,10 @@
# Testing Certificates
This is a collection of certificates useful for testing aspects of Requests'
behaviour.
The certificates include:
* [expired](./expired) server certificate with a valid certificate authority
* [mtls](./mtls) provides a valid client certificate with a 2 year validity
* [valid](./valid) has a valid server certificate

View File

@@ -0,0 +1,13 @@
.PHONY: all clean ca server
ca:
make -C $@ all
server:
make -C $@ all
all: ca server
clean:
make -C ca clean
make -C server clean

View File

@@ -0,0 +1,11 @@
# Expired Certificates and Configuration for Testing
This has a valid certificate authority in [ca](./ca) and an invalid server
certificate in [server](./server).
This can all be regenerated with:
```
make clean
make all
```

View File

@@ -0,0 +1,13 @@
.PHONY: all clean
root_files = ca-private.key ca.crt
ca-private.key:
openssl genrsa -out ca-private.key 2048
all: ca-private.key
openssl req -x509 -sha256 -days 7300 -key ca-private.key -out ca.crt -config ca.cnf
ln -s ca.crt cacert.pem
clean:
rm -f cacert.pem ca.crt ca-private.key *.csr

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCfZUh82dF/r9GW
89IN2vqOiMMuikIAO3SEI3+uSGCdWT13C+NnrFJ7XF/D6UGy1mvm8KfhSnapWoAk
toyPXSc/GNzJzCwZool7xE+rm/0vbu1XbUcQcqB8nQRLzTChDIGuuD8DHs7bmen1
9sT5kZy0CIqac383cQxR8W1Fs48xEBJfuBBmyl+bz4ugPci96H4DIuAD2QvP2KKg
Gqs4yyDPSmf86k9+okOsLMQVwGnHety+TPJawCn2aCXl+rmMTSCH2sUEc81cXaVQ
Yxyf6HaqGncCs1O2MzeRbPugEzb5K4ZVM4NYtDMkxrQnZFCALf4XOma1uv5Kh6Qo
FMFHOA6tAgMBAAECgf9YadXLawbJzLx0/smE5fIVHccmCYqSlmgK46XvBjaREO8H
GZEJ8IvP4W09PiUzDbzMXLDCRouLZKevtZJB82nRlfjh9l5/2aho/nsytVO6+8yq
sfK5LNvYQ0Aey7ItosJMJ+bL1ErphHZB+J9Jz3scYaCAC5CFMC+lREVYZEEI9QD4
P2D5QbmaSeu8jmL/H3fWHjNXWDprue3W/MIf96NZa3qJew45go4TAYYMe5i757KW
Ja40VNfmsgbz4uI9oDXaYL/NkWUaQP1lnh+Mfrm1YnBe2wsLcP/WuM5h0bYzJW/1
ZeSrZM3fqCMW6SJyrVE1qzqvtw1xQBlrq0B6q0ECgYEA0fi4+ySFGR+mL6k5UjP1
roREqQgKaLgdhOvD88EnO93Nl6tJ3Qk8LyzPUNbxe1/xTUEKMtglBKOoxCHJJZlg
xXnKBAQUtlmrLFKIGe+UCD+r+wfSpS6Sl7BUDmeCSczG9dPN5vnyZA4ixUke2SCC
k4Eb9Q0AHyNnbXv928r0sfkCgYEAwlZRYmGTVva6cY2YEmMrqbWy4Wxm2Zmdo+Uq
Xu1RZF9a3tGzNbGsyYdeLNY7vVZoVOm1paMJCA8ScNLFtCux2jEPqwqd1OZ8OLhA
1VF3/kYtUSdqwLzWoS1RdD6mZCAHeOE+N0pone4lt3A2o8wtpHsaDA+XSTw2rHLR
LVS+b1UCgYEAtezJ4Ze31pfMdrkpmCa69JVXpBj6Y9c6hGN+aWFuq/k22/WmTuRk
h/9MNR+3JQ1w1l3HB1ytXkKqxBz92hz1csReG3Kpu4EfxYxQriAdY7Q/P4Z8pXAf
xVwayEw439aUgIQef8UKllSFHeiH2NrJKCKSZZT5CQG06HCo+Fn1/4kCgYAYuwtY
TbqGUpefY7l6fYxM6IZ/EWB1SIs7FCq0MdctwsS5nk4EAzxN2SAu7IRlr91PEP7A
uWKo1+Is4WWva/ASKDQqPAuh0EL2pNv7SYbPoPabYTzAkkdt82puNJrQGxNYWrGk
L5/omSnLkkghyBX23IOQDVvfQf5jK6la73HckQKBgAI+iLECAkle9HvnJ3flicau
9FAU1/9pOdM+WogSanhYQ/P2rAwRiyCIkqu62/OoZR5g4kLxWqOOmVvsK3j+gs5F
FtwN7gauq06MAHnWr6qC8ZltzMsGZTVDvqSH2vgV4T1V6ovVpTBPKQ1gWtABEmpm
dyfeA6HHeRAHx8VRGpL6
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,17 @@
[req]
default_bits = 2048
prompt = no
default_md = sha256
encrypt_key = no
distinguished_name = dn
x509_extensions = v3_ca
[dn]
C = US # country code
O = Python Software Foundation # organization
OU = python-requests # organization unit/department
CN = Self-Signed Root CA # common name / your cert name
[v3_ca]
basicConstraints = critical, CA:true
keyUsage = critical, cRLSign, digitalSignature, keyCertSign

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIUQt0yyZmppkHKNx4aXRrmD5tvjbswDQYJKoZIhvcNAQEL
BQAwajELMAkGA1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu
ZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYt
U2lnbmVkIFJvb3QgQ0EwHhcNMjUwMzI5MTM1MTQ1WhcNNDUwMzI0MTM1MTQ1WjBq
MQswCQYDVQQGEwJVUzEjMCEGA1UECgwaUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRp
b24xGDAWBgNVBAsMD3B5dGhvbi1yZXF1ZXN0czEcMBoGA1UEAwwTU2VsZi1TaWdu
ZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ9lSHzZ
0X+v0Zbz0g3a+o6Iwy6KQgA7dIQjf65IYJ1ZPXcL42esUntcX8PpQbLWa+bwp+FK
dqlagCS2jI9dJz8Y3MnMLBmiiXvET6ub/S9u7VdtRxByoHydBEvNMKEMga64PwMe
ztuZ6fX2xPmRnLQIippzfzdxDFHxbUWzjzEQEl+4EGbKX5vPi6A9yL3ofgMi4APZ
C8/YoqAaqzjLIM9KZ/zqT36iQ6wsxBXAacd63L5M8lrAKfZoJeX6uYxNIIfaxQRz
zVxdpVBjHJ/odqoadwKzU7YzN5Fs+6ATNvkrhlUzg1i0MyTGtCdkUIAt/hc6ZrW6
/kqHpCgUwUc4Dq0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAYYwHQYDVR0OBBYEFAhGiD3+10LBrdMW3+j/ceXMXSqcMA0GCSqGSIb3DQEB
CwUAA4IBAQBRT21cyZ0Jx0JLA2ilYTLvpMsSryGyWrCOXlmRlBt1MAhmxdTRgCmu
UB3UU2pfnrC16UeMVVS411lhzjowFXkXrjAqSUBRcetUIYHlpnGgDdUl4dV/X5kx
HxD9VUBx/QwGeyzFhjzjeN89M2v9kPnhU/kkVfcsafwYiHdC6pwN6zeZNz7JP+GS
rmI+KVpm5C+Nz6ekm3TR8rFgPIsiDTbY3qj/DNYX2+NhpU1DZfm687vhOr3Ekljx
NHNu9++STEjGpirrI8EqQnK+FP2fRJ5D82YZM0d++8tmHKpY0+FRCr8//459sgun
CojmhIobDa2NuF81Jx6Cc7lagCPG3/Ts
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
4F36C3A7E075BA6452D10EEB81E7F189FF489B83

View File

@@ -0,0 +1,16 @@
.PHONY: all clean
server.key:
openssl genrsa -out $@ 2048
server.csr: server.key
openssl req -key $< -new -out $@ -config cert.cnf
server.pem: server.csr
openssl x509 -req -CA ../ca/ca.crt -CAkey ../ca/ca-private.key -in server.csr -outform PEM -out server.pem -days 0 -CAcreateserial
openssl x509 -in ../ca/ca.crt -outform PEM >> $@
all: server.pem
clean:
rm -f server.*

View File

@@ -0,0 +1,24 @@
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt=no
[req_distinguished_name]
C = US
ST = DE
O = Python Software Foundation
OU = python-requests
CN = localhost
[v3_req]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
DNS.1 = localhost
IP.1 = 127.0.0.1
IP.2 = ::1

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIDHjCCAgYCAQAwbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK
DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl
cXVlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDcRRwk8IU1YoNu8CHzB5Vh8HP/yLfBtU69LLZq+7rDG31JlR5s
lmcwLLoZ8opUQ5rg8JMRZ7toh5zB4Uc0B4Sg8RhQMSOZYBIJkXdHuqQkciR0vWnN
vD/5CkWEhnj4dxE7xTbDufBlxmwAthC/u72UIsZHavAyLCBqsONa7xuTiogz/d+3
G+525JrfVr05hhJpT4Ypx5YY+ABkIOuOk/XuudbGm5SquuX6BgjmgaGtDjAuE/Rn
BnliIavCDrG0v3KGbG3Xxt7lFTu+98foForwGCbbQBraty27oZrzAtLltfIHlJRn
jPz0JA9akNDhAihxEsTUhk2d7jFszsd0Ev7DAgMBAAGgbDBqBgkqhkiG9w0BCQ4x
XTBbMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
MCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAN
BgkqhkiG9w0BAQsFAAOCAQEAVVIxJlDrgeG0bOSufgVVRqDQx7XSd15mGlT+CynM
lEFJ3Q9k98T2vRNNGVYYYZAnbdSOW9ACwWGcYm2bzIjbgZV0H2Kz0dLD/GrNuEY+
O9j6K2toFKc57G7UUkve+N74ldq+hkR4zbb6FQmTlnL2YaPp2dv5TxdMKfHEfPNf
Bg8xpbXdoRc7CYW1ZACme+d2U063GVqQsrIfwGJ+BtE6aNo62T/oEm+G4Wy5iBay
jNv/imwf+JKQ75bTvha9YLUg2scqdYwJj8JlBw7cvkBIHW8GydA3fX4dtV9YBbFi
8RTlWhhLgCXpYbLoDGOqF6f/MuPSIGkV1wVhCUfYA+p+Gw==
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcRRwk8IU1YoNu
8CHzB5Vh8HP/yLfBtU69LLZq+7rDG31JlR5slmcwLLoZ8opUQ5rg8JMRZ7toh5zB
4Uc0B4Sg8RhQMSOZYBIJkXdHuqQkciR0vWnNvD/5CkWEhnj4dxE7xTbDufBlxmwA
thC/u72UIsZHavAyLCBqsONa7xuTiogz/d+3G+525JrfVr05hhJpT4Ypx5YY+ABk
IOuOk/XuudbGm5SquuX6BgjmgaGtDjAuE/RnBnliIavCDrG0v3KGbG3Xxt7lFTu+
98foForwGCbbQBraty27oZrzAtLltfIHlJRnjPz0JA9akNDhAihxEsTUhk2d7jFs
zsd0Ev7DAgMBAAECggEAUZjCX8a/ufJ3+OE42lQdWO4fsonS1I3LENYe70u4OC2X
QGpenmAq8pQnDpSj/KocliZYfLKeII9YGRRQcaw1S/9z/8TsSJVnqSa7dpVj1+J2
sc43AxEw65sL/Jdp+bT169vXOTNIpBMYkDzhwH0WMemd5Pfu6c8h5RQI7Pc1knYn
nPNY848qSYCWOUjZS3QmBik/gp9X//yxVCyvxB3xVnb1cpvc952D90Va6nFIWfgN
ix4NgFgAvwIxCFpWI2z7JF8uBdAHPeFAx8pFukQpAzwhaEILlgt3WbvEob9CsdP5
E39SUkzxiIfVM1du+hRquk9SJ/X56OSLnEjxLarSIQKBgQDyVU00yrv7lG7TJLZ5
YyfE20A7eUtYdcfatTKfsxKO4KVV+p0mGsNnFhhKziuT5/xtVunbUNFEOaUfmTUP
dCjy4XkDbE/ufxzIm1WTSqknUluVoJRWKmLI5gbDFxu/XGRQNLxQbMK54l3N69PT
EO4kz/jqBYbd4aEmtaSx2R8JowKBgQDosUTgBGDxDE2ODPVoXc+YjReOmVHvGMKK
tA+KDBEs4spxaqhH0OFh6mewx3jA4VHbcIgM50STXrq1VL/QqYclDlY9/VWzkmqp
2Ekc4NoAl3H022pgcbmXx3qapC4Z3hokNFOlbtD9xQf9NMx6c5djTKMYTBrBBWXH
oFhSz6PIYQKBgQCMGqkydmvMffq89CLTd3JMq/4s5GmdUSsk1VHZZuy50kOEvAoT
N7H1bZ7J0Pz83Eji5jb6Z3U1nqZK6Ib20k/CbH1Mb1ifKLp5eOU27Rly9Hiiv15D
munWALe0Hy4Zqs8MWBDv5pGGasuU/F1RUB5/BgaBNoTMz2AeQzJe6Iq7RQKBgCca
Ku3OLpAzNhEp4k9wfEMxaoT/BMK+EWsHiRj0oCo/zi8y8iZnVoiCwHv3eTZIZt4O
Uf6BGof9QjjYjgc9hcVXXGy8Vpt/fkceXmLo8hlpWbAA8yZT1hFIZzT3Y/va09/D
n07MiXgrlQUay0XEiOsZ5Mpfd5t6EblzG4SG+gnhAoGAZ+shbacxhji7cUUYJLrO
9uZJCDCZiyq8yZ+lMzX1kILQwaP6VmSvF4TzKrkrCuD2HzUYcqebko/TvckeTp/a
oYC2put3zt0CHBf/keeeJwjhff19qVyE9mpZwoo7PuS5zmM7pQOLxzCyAM9MdsCz
kmnbborcfh74fkfRcwXm6G8=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,44 @@
-----BEGIN CERTIFICATE-----
MIIDpzCCAo+gAwIBAgIUTzbDp+B1umRS0Q7rgefxif9Im4EwDQYJKoZIhvcNAQEL
BQAwajELMAkGA1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu
ZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYt
U2lnbmVkIFJvb3QgQ0EwHhcNMjUwMjE3MDAzODIyWhcNMjUwMjE3MDAzODIyWjBt
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0
d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxEjAQBgNV
BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxF
HCTwhTVig27wIfMHlWHwc//It8G1Tr0stmr7usMbfUmVHmyWZzAsuhnyilRDmuDw
kxFnu2iHnMHhRzQHhKDxGFAxI5lgEgmRd0e6pCRyJHS9ac28P/kKRYSGePh3ETvF
NsO58GXGbAC2EL+7vZQixkdq8DIsIGqw41rvG5OKiDP937cb7nbkmt9WvTmGEmlP
hinHlhj4AGQg646T9e651sablKq65foGCOaBoa0OMC4T9GcGeWIhq8IOsbS/coZs
bdfG3uUVO773x+gWivAYJttAGtq3LbuhmvMC0uW18geUlGeM/PQkD1qQ0OECKHES
xNSGTZ3uMWzOx3QS/sMCAwEAAaNCMEAwHQYDVR0OBBYEFFK1WmMqRSzUD2isk8TL
M3JfsVczMB8GA1UdIwQYMBaAFAhGiD3+10LBrdMW3+j/ceXMXSqcMA0GCSqGSIb3
DQEBCwUAA4IBAQCekjxplL/AI32WtODgw7FpTXNXdyNy8PWEhn3ufL0MqiyseYOZ
bIa0PAecsArlKs5hXJzB7p/hu5CZdvaCButw1jyWQBySCpeJXn3FmGdTkBvhwBHv
y6npmBoy/nbLkIRNRcoLbALlfn/0iGWfmDTRblT7vRNWJmZCZCTA/+ILXJ36ItbF
3JCs3ARF6XWORuZs5Y8cNloOy2brAC/4EHnTfOZBQf8cL8CfHlcNa+nbG1j+wVNO
60e/0v9zTxa2wNzdnLBCW4rqJFJ44aGClxat5tWuypv0snA/0xrqIYWTQGXZPVT6
rET47dfVbj1QxmW3sAyuy5PskZA9T7pOhcjR
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIUG/CTOPIQbH2BI36TyThUChQyR8wwDQYJKoZIhvcNAQEL
BQAwajELMAkGA1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu
ZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYt
U2lnbmVkIFJvb3QgQ0EwHhcNMjUwMjE3MDAzODIyWhcNNDUwMjEyMDAzODIyWjBq
MQswCQYDVQQGEwJVUzEjMCEGA1UECgwaUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRp
b24xGDAWBgNVBAsMD3B5dGhvbi1yZXF1ZXN0czEcMBoGA1UEAwwTU2VsZi1TaWdu
ZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ9lSHzZ
0X+v0Zbz0g3a+o6Iwy6KQgA7dIQjf65IYJ1ZPXcL42esUntcX8PpQbLWa+bwp+FK
dqlagCS2jI9dJz8Y3MnMLBmiiXvET6ub/S9u7VdtRxByoHydBEvNMKEMga64PwMe
ztuZ6fX2xPmRnLQIippzfzdxDFHxbUWzjzEQEl+4EGbKX5vPi6A9yL3ofgMi4APZ
C8/YoqAaqzjLIM9KZ/zqT36iQ6wsxBXAacd63L5M8lrAKfZoJeX6uYxNIIfaxQRz
zVxdpVBjHJ/odqoadwKzU7YzN5Fs+6ATNvkrhlUzg1i0MyTGtCdkUIAt/hc6ZrW6
/kqHpCgUwUc4Dq0CAwEAAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
CEaIPf7XQsGt0xbf6P9x5cxdKpwwDQYJKoZIhvcNAQELBQADggEBAHMgyQNA3DQG
0l9eX8RMl4YNwhAXChU/wOTcvD+F4OsJcPqzy6QHFh1AbBBGOI9/HN7du+EiKwGZ
wE+69FEsSbhSv22XidPm+kHPTguWl1eKveeWrrT5MPl9F48s14lKb/8yMEa1/ryG
Iu8NQ6ZL91JbTXdkLoBDya9HZqyXjwcGkXLE8fSqTibJ7EhWS5Q3Ic7WPgUoGAum
b5ygoxqhm+SEyXC2/LAktwmFawkv1SsMeYpT790VIFqJ/TVVnUl+gQ2RjSEl2WLb
UO4Hwq4FZbWF9NrY6JVThLmbcr8eW6+UxWfiXHLw/qTRre4/3367QAUQRt7EuEsb
KOWpOS3fbsI=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,7 @@
.PHONY: all clean
all:
make -C client all
clean:
make -C client clean

View File

@@ -0,0 +1,4 @@
# Certificate Examples for mTLS
This has some generated certificates for mTLS utilization. The idea is to be
able to have testing around how Requests handles client certificates.

View File

@@ -0,0 +1,16 @@
.PHONY: all clean
client.key:
openssl genrsa -out $@ 2048
client.csr: client.key
openssl req -key $< -new -out $@ -config cert.cnf
client.pem: client.csr
openssl x509 -req -CA ./ca/ca.crt -CAkey ./ca/ca-private.key -in client.csr -outform PEM -out client.pem -days 730 -CAcreateserial
openssl x509 -in ./ca/ca.crt -outform PEM >> $@
all: client.pem
clean:
rm -f client.*

1
tests/certs/mtls/client/ca Symbolic link
View File

@@ -0,0 +1 @@
../../expired/ca/

View File

@@ -0,0 +1,26 @@
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt=no
[req_distinguished_name]
C = US
ST = DE
O = Python Software Foundation
OU = python-requests
CN = requests
[v3_req]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ::1
URI.1 = spiffe://trust.python.org/v0/maintainer/sigmavirus24/project/requests/org/psf
URI.2 = spiffe://trust.python.org/v1/maintainer:sigmavirus24/project:requests/org:psf
URI.3 = spiffe://trust.python.org/v1/maintainer=sigmavirus24/project=requests/org=psf

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEGjCCAwICAQAwbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK
DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl
cXVlc3RzMREwDwYDVQQDDAhyZXF1ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAJrdvu5kvCy5g6w67gczETk/u6K0UBmf6Mv0lqXCp3Voyt5fv6SI
/OPWrFV1+m4imNe5bLM7JUoUfkqkmULsjTTh7sxVLjW226vLSYOWWy7PA+OSwUTN
LjydF1dazlPedHPYdmhVShCmyoOd1pUCDQn0/4cEA/WW4mtzZImmoCf/pyAM3XAC
9RBcSSJRywOTe6n9LY6Ko0YUW94ay2M4ClVblDxswDemaAsLFuciKmi53gKx4H/l
areo8p60dgubooiMbcc4E9bzp0oJpfh7xhwKeJtCNEpOik1AiiQIZtwqmkkIHvtI
Go3SZ7WAQU9Eh2r+u3E6aSl+N0PMXK4Y4JsCAwEAAaCCAWcwggFjBgkqhkiG9w0B
CQ4xggFUMIIBUDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDAjCCAR8GA1UdEQSCARYwggESggsqLmxvY2FsaG9zdIcEfwAAAYcQAAAA
AAAAAAAAAAAAAAAAAYZNc3BpZmZlOi8vdHJ1c3QucHl0aG9uLm9yZy92MC9tYWlu
dGFpbmVyL3NpZ21hdmlydXMyNC9wcm9qZWN0L3JlcXVlc3RzL29yZy9wc2aGTXNw
aWZmZTovL3RydXN0LnB5dGhvbi5vcmcvdjEvbWFpbnRhaW5lcjpzaWdtYXZpcnVz
MjQvcHJvamVjdDpyZXF1ZXN0cy9vcmc6cHNmhk1zcGlmZmU6Ly90cnVzdC5weXRo
b24ub3JnL3YxL21haW50YWluZXI9c2lnbWF2aXJ1czI0L3Byb2plY3Q9cmVxdWVz
dHMvb3JnPXBzZjANBgkqhkiG9w0BAQsFAAOCAQEAMOwYPyq+OpMRQUAgoRjfxTQO
DiqfBzCsjUsPAacLTtebbWBx8y6TkLSb+/Qn3amq3ESo4iBqKpmVwdlAS4P486GD
9f78W3zkZ29jGcnQ+XHb7WvPvzBRoXImE276F9JGqJ+9q39Cbxzh0U2+ofBx2iGY
sSutzU0B/l/FKZRc8thuFoeKqHwVePLGD9p2+2nYI9I08QoGqEokTcFAq0tZ858F
9PdxBZYOKOMpnLZhiJ8qZo23v3ycBXPOjg5ILtQ9EzHoNEA5Mxx/mfNLKJ0NktZD
KXANLWKbXm+w9gTcBLtCPWNeqj5DIGPsHSfq/Bmjbp/o+uOJs6oq3s5rA7WrAA==
-----END CERTIFICATE REQUEST-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCa3b7uZLwsuYOs
Ou4HMxE5P7uitFAZn+jL9Jalwqd1aMreX7+kiPzj1qxVdfpuIpjXuWyzOyVKFH5K
pJlC7I004e7MVS41ttury0mDllsuzwPjksFEzS48nRdXWs5T3nRz2HZoVUoQpsqD
ndaVAg0J9P+HBAP1luJrc2SJpqAn/6cgDN1wAvUQXEkiUcsDk3up/S2OiqNGFFve
GstjOApVW5Q8bMA3pmgLCxbnIipoud4CseB/5Wq3qPKetHYLm6KIjG3HOBPW86dK
CaX4e8YcCnibQjRKTopNQIokCGbcKppJCB77SBqN0me1gEFPRIdq/rtxOmkpfjdD
zFyuGOCbAgMBAAECggEABHa5xyNOLTfXrMIyFDELoQvOO71YxbRPQHm3UeXPb9nq
Zwh5fKOaLnMEmp4A7uW+ZBFrKatdwsnebgZaiIxK8ahFesxFvbScllIQt2NBE5NR
+GBFg9cqKwMYJiNu6Qnzb1dg6lbzAJHeKncFNVxOxeey6dBa0NxdgF1eG32bBiwT
yIr6JO7cK5JfcExls5yxdZMZiW08quaeFMQR6Wocod2caDgJUBVbZwivEoNI3ak4
/kzQaxvRoDMM8uN3LrVH2YUJgURUxSbpOLu8ycZtfg8JRArprUYI0P0OD6iDlly1
3FhJXqBEY0dWQPmZKP/Elt3ywC1oncW+AqPh9cchiQKBgQDMriOKoBrTmplw0kbo
EEuxTU1A23BqcZfn2+PoUww6UxOvStL/s7iupjl3g4CXw0RgIfyOuJQldLdq7mh6
W7BpK855fH2CLy1VMdIFY1umHSgJ5Bayd5NmyKdfORUw88PTFFSARYyr5DDqufWg
jI77BMjqHtbyujQKspV+9U66ZwKBgQDBsi5YptfaqMdzh343G5zAtCiGCd3m9NZO
atEEvgq9LKqEVwvJ/FD+3QPAS1MN+ms9KcfJ3G05VUhhEsPGRKibgcE0FNA/VBDO
jS2HK6kZ1M0clC5kHmQfLZxp1q3tA5nW6zqjVFdqYzJsis7G5YxFKhnzui0lgP6V
I1Io+3QvrQKBgQCK6D+sq92o8AnkfICsq6qDCKA+PO68/pyGOUAiAoKQ7qK0W0Z5
TMIwnRTxHCjgViAIUehx/6hjByQXiPcU2zcNGTLGVgtjl5rfb7FGANlJEg6DL+2L
bwV1QwX75OSR1U136hsy9oByg6oDEvM040+B4gxsf0OHdYEuJWa5w8eLTwKBgCJt
CM+416SFWu2tp0EkJzgYzRsFpermmTBWy8+L91yoE6Zx0iaUMdEadxA2Uwyo9WZp
hpjaFI+cGMEoFKOokE8TQMOA74JR7qrHbNAZcnSk3c+2hohE3oasFKC7By6Y9T69
kC53TxIZj1y7TwUKx2ODmBk5fcysoJLhNDkUeBIBAoGAQ/YMeMN/pSvTDCPhubbk
9bF0SwoN6DWrzw7gzMMhHlVSPM4fjdBA+wXbhnYF7hiNan7uShtRHVhwyfj+glBz
KIkDxETjxTWx5mbo3tNf8xEMsbHZBe8KPlKxizdAOvShLvkpthTCPiyqn6XTvbs4
vC0zZrsQWCYT4Wbm7gcOOAU=
-----END PRIVATE KEY-----

Some files were not shown because too many files have changed in this diff Show More