Publish GraphShieldMistral interactive explorer (part 5)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/INSTALLER +1 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/METADATA +110 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/RECORD +885 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/REQUESTED +0 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/WHEEL +4 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/entry_points.txt +4 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/AUTHORS.txt +888 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt +20 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
- .venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/connection.py +260 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/emscripten_fetch_worker.js +110 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/fetch.py +726 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/request.py +22 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/response.py +281 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +563 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/socks.py +228 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/exceptions.py +335 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/fields.py +341 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/filepost.py +89 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/__init__.py +53 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/connection.py +356 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/probe.py +87 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/poolmanager.py +653 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/py.typed +2 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/response.py +1487 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/__init__.py +42 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/connection.py +137 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/proxy.py +43 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/request.py +254 -0
- .venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/response.py +101 -0
.gitattributes
CHANGED
|
@@ -4,3 +4,4 @@
|
|
| 4 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 5 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 6 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 4 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 5 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 6 |
.venv/lib/python3.14/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
.venv/lib/python3.14/site-packages/yaml/_yaml.cpython-314-darwin.so filter=lfs diff=lfs merge=lfs -text
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/METADATA
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: pip
|
| 3 |
+
Version: 26.2.1
|
| 4 |
+
Summary: The PyPA recommended tool for installing Python packages.
|
| 5 |
+
Author-email: The pip developers <distutils-sig@python.org>
|
| 6 |
+
Requires-Python: >=3.10
|
| 7 |
+
Description-Content-Type: text/x-rst
|
| 8 |
+
License-Expression: MIT
|
| 9 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 10 |
+
Classifier: Intended Audience :: Developers
|
| 11 |
+
Classifier: Topic :: Software Development :: Build Tools
|
| 12 |
+
Classifier: Programming Language :: Python
|
| 13 |
+
Classifier: Programming Language :: Python :: 3
|
| 14 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.15
|
| 21 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 22 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 23 |
+
License-File: AUTHORS.txt
|
| 24 |
+
License-File: LICENSE.txt
|
| 25 |
+
License-File: src/pip/_vendor/cachecontrol/LICENSE.txt
|
| 26 |
+
License-File: src/pip/_vendor/certifi/LICENSE
|
| 27 |
+
License-File: src/pip/_vendor/distlib/LICENSE.txt
|
| 28 |
+
License-File: src/pip/_vendor/distro/LICENSE
|
| 29 |
+
License-File: src/pip/_vendor/idna/LICENSE.md
|
| 30 |
+
License-File: src/pip/_vendor/msgpack/COPYING
|
| 31 |
+
License-File: src/pip/_vendor/packaging/LICENSE
|
| 32 |
+
License-File: src/pip/_vendor/packaging/LICENSE.APACHE
|
| 33 |
+
License-File: src/pip/_vendor/packaging/LICENSE.BSD
|
| 34 |
+
License-File: src/pip/_vendor/pkg_resources/LICENSE
|
| 35 |
+
License-File: src/pip/_vendor/platformdirs/LICENSE
|
| 36 |
+
License-File: src/pip/_vendor/pygments/LICENSE
|
| 37 |
+
License-File: src/pip/_vendor/pyproject_hooks/LICENSE
|
| 38 |
+
License-File: src/pip/_vendor/requests/LICENSE
|
| 39 |
+
License-File: src/pip/_vendor/resolvelib/LICENSE
|
| 40 |
+
License-File: src/pip/_vendor/rich/LICENSE
|
| 41 |
+
License-File: src/pip/_vendor/tomli/LICENSE
|
| 42 |
+
License-File: src/pip/_vendor/tomli_w/LICENSE
|
| 43 |
+
License-File: src/pip/_vendor/truststore/LICENSE
|
| 44 |
+
License-File: src/pip/_vendor/urllib3/LICENSE.txt
|
| 45 |
+
Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
|
| 46 |
+
Project-URL: Documentation, https://pip.pypa.io
|
| 47 |
+
Project-URL: Homepage, https://pip.pypa.io/
|
| 48 |
+
Project-URL: Source, https://github.com/pypa/pip
|
| 49 |
+
|
| 50 |
+
pip - The Python Package Installer
|
| 51 |
+
==================================
|
| 52 |
+
|
| 53 |
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/pip.svg
|
| 54 |
+
:target: https://pypi.org/project/pip/
|
| 55 |
+
:alt: PyPI
|
| 56 |
+
|
| 57 |
+
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/pip
|
| 58 |
+
:target: https://pypi.org/project/pip
|
| 59 |
+
:alt: PyPI - Python Version
|
| 60 |
+
|
| 61 |
+
.. |docs-badge| image:: https://readthedocs.org/projects/pip/badge/?version=latest
|
| 62 |
+
:target: https://pip.pypa.io/en/latest
|
| 63 |
+
:alt: Documentation
|
| 64 |
+
|
| 65 |
+
|pypi-version| |python-versions| |docs-badge|
|
| 66 |
+
|
| 67 |
+
Pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
|
| 68 |
+
|
| 69 |
+
Please take a look at our documentation for how to install and use pip:
|
| 70 |
+
|
| 71 |
+
* `Installation`_
|
| 72 |
+
* `Usage`_
|
| 73 |
+
|
| 74 |
+
We release updates regularly, with a new version every 3 months. Find more details in our documentation:
|
| 75 |
+
|
| 76 |
+
* `Release notes`_
|
| 77 |
+
* `Release process`_
|
| 78 |
+
|
| 79 |
+
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
|
| 80 |
+
|
| 81 |
+
* `Issue tracking`_
|
| 82 |
+
* `Discourse channel`_
|
| 83 |
+
* `User IRC`_
|
| 84 |
+
|
| 85 |
+
If you want to get involved, head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
|
| 86 |
+
|
| 87 |
+
* `GitHub page`_
|
| 88 |
+
* `Development documentation`_
|
| 89 |
+
* `Development IRC`_
|
| 90 |
+
|
| 91 |
+
Code of Conduct
|
| 92 |
+
---------------
|
| 93 |
+
|
| 94 |
+
Everyone interacting in the pip project's codebases, issue trackers, chat
|
| 95 |
+
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
|
| 96 |
+
|
| 97 |
+
.. _package installer: https://packaging.python.org/guides/tool-recommendations/
|
| 98 |
+
.. _Python Package Index: https://pypi.org
|
| 99 |
+
.. _Installation: https://pip.pypa.io/en/stable/installation/
|
| 100 |
+
.. _Usage: https://pip.pypa.io/en/stable/
|
| 101 |
+
.. _Release notes: https://pip.pypa.io/en/stable/news.html
|
| 102 |
+
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
|
| 103 |
+
.. _GitHub page: https://github.com/pypa/pip
|
| 104 |
+
.. _Development documentation: https://pip.pypa.io/en/latest/development
|
| 105 |
+
.. _Issue tracking: https://github.com/pypa/pip/issues
|
| 106 |
+
.. _Discourse channel: https://discuss.python.org/c/packaging
|
| 107 |
+
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
|
| 108 |
+
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
|
| 109 |
+
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
|
| 110 |
+
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/RECORD
ADDED
|
@@ -0,0 +1,885 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/pip,sha256=XeGIH1LcpBCVlHyzVMhEwBtPhmEMwfq4otwt7tB9uFo,229
|
| 2 |
+
../../../bin/pip3,sha256=XeGIH1LcpBCVlHyzVMhEwBtPhmEMwfq4otwt7tB9uFo,229
|
| 3 |
+
../../../bin/pip3.14,sha256=XeGIH1LcpBCVlHyzVMhEwBtPhmEMwfq4otwt7tB9uFo,229
|
| 4 |
+
pip-26.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 5 |
+
pip-26.2.1.dist-info/METADATA,sha256=OlYQBYIoQfLEX0QvbHp1BRcsjDdsn-J02HbIrJEpaMs,4617
|
| 6 |
+
pip-26.2.1.dist-info/RECORD,,
|
| 7 |
+
pip-26.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 8 |
+
pip-26.2.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
| 9 |
+
pip-26.2.1.dist-info/entry_points.txt,sha256=Vhf8s0IYgX37mtd4vGL73BPcxdKnqeCFPzB5-d30x8o,84
|
| 10 |
+
pip-26.2.1.dist-info/licenses/AUTHORS.txt,sha256=TrH7FwXi3viY6BvLoZw6rIBiDSe6NWnWpt97RinRtcI,12139
|
| 11 |
+
pip-26.2.1.dist-info/licenses/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093
|
| 12 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt,sha256=hu7uh74qQ_P_H1ZJb0UfaSQ5JvAl_tuwM2ZsMExMFhs,558
|
| 13 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
|
| 14 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt,sha256=gI4QyKarjesUn_mz-xn0R6gICUYG1xKpylf-rTVSWZ0,14531
|
| 15 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
|
| 16 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
|
| 17 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING,sha256=SS3tuoXaWHL3jmCRvNH-pHTWYNNay03ulkuKqz8AdCc,614
|
| 18 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
|
| 19 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
|
| 20 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
|
| 21 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
|
| 22 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
|
| 23 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331
|
| 24 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
|
| 25 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
| 26 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE,sha256=84j9OMrRMRLB3A9mm76A5_hFQe26-3LzAw0sp2QsPJ0,751
|
| 27 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE,sha256=3u18F6QxgVgZCj6iOcyHmlpQJxzruYrnAl9I--WNyhU,1056
|
| 28 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
|
| 29 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
|
| 30 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE,sha256=M757fo-k_Rmxdg4ajtimaL2rhSyRtpLdQUJLy3Jan8o,1086
|
| 31 |
+
pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093
|
| 32 |
+
pip/__init__.py,sha256=BKp2zqsPQ0HiktquNWN_L4cYsYMxPML7WI0TBMucBEM,355
|
| 33 |
+
pip/__main__.py,sha256=rOZRtrXjDBzY24niaxTnd9ZHHWL7B0EawVdLoJ3nI6c,874
|
| 34 |
+
pip/__pip-runner__.py,sha256=720Mt6h07Uce52v80EOF__JYauoLw9b7Pfs_5B91isg,1451
|
| 35 |
+
pip/__pycache__/__init__.cpython-314.pyc,,
|
| 36 |
+
pip/__pycache__/__main__.cpython-314.pyc,,
|
| 37 |
+
pip/__pycache__/__pip-runner__.cpython-314.pyc,,
|
| 38 |
+
pip/_internal/__init__.py,sha256=S7i9Dn9aSZS0MG-2Wrve3dV9TImPzvQn5jjhp9t_uf0,511
|
| 39 |
+
pip/_internal/__pycache__/__init__.cpython-314.pyc,,
|
| 40 |
+
pip/_internal/__pycache__/cache.cpython-314.pyc,,
|
| 41 |
+
pip/_internal/__pycache__/configuration.cpython-314.pyc,,
|
| 42 |
+
pip/_internal/__pycache__/exceptions.cpython-314.pyc,,
|
| 43 |
+
pip/_internal/__pycache__/main.cpython-314.pyc,,
|
| 44 |
+
pip/_internal/__pycache__/pyproject.cpython-314.pyc,,
|
| 45 |
+
pip/_internal/__pycache__/self_outdated_check.cpython-314.pyc,,
|
| 46 |
+
pip/_internal/__pycache__/wheel_builder.cpython-314.pyc,,
|
| 47 |
+
pip/_internal/build_env/__init__.py,sha256=AI5x2Kfv1exG1IbsNHuIV1agqNgstTj_LWpbwWLq8ok,788
|
| 48 |
+
pip/_internal/build_env/__pycache__/__init__.cpython-314.pyc,,
|
| 49 |
+
pip/_internal/build_env/__pycache__/base.cpython-314.pyc,,
|
| 50 |
+
pip/_internal/build_env/__pycache__/installer.cpython-314.pyc,,
|
| 51 |
+
pip/_internal/build_env/__pycache__/noop.cpython-314.pyc,,
|
| 52 |
+
pip/_internal/build_env/__pycache__/venv.cpython-314.pyc,,
|
| 53 |
+
pip/_internal/build_env/__pycache__/virtual.cpython-314.pyc,,
|
| 54 |
+
pip/_internal/build_env/base.py,sha256=7RHW5X-QSVWHeK7_5Uei9Gbq0q1pAXHF9tq7Mj3wtTs,3440
|
| 55 |
+
pip/_internal/build_env/installer.py,sha256=eMUytQCdBiQ9TgkaGeTAzq_IVBqLHyD9OJ_kNTIjjcM,13046
|
| 56 |
+
pip/_internal/build_env/noop.py,sha256=FXk9ZFTTN0s1cecZE6ycN2ZznBh75Ok2I3QXF8HTo6I,1001
|
| 57 |
+
pip/_internal/build_env/venv.py,sha256=PsDl0PpmB9jRNaIawhy6Xk9R8WcYoAogLS2afmZ9AQc,5692
|
| 58 |
+
pip/_internal/build_env/virtual.py,sha256=HsVRzzhgBGyy06DZBoO0PW_3kIEacy9S4TI5H1q72Hk,4858
|
| 59 |
+
pip/_internal/cache.py,sha256=eqMQVfpD8x7GcCO0V0z73mnjPIuSapMFoQ_WRE7iLY8,10457
|
| 60 |
+
pip/_internal/cli/__init__.py,sha256=Iqg_tKA771XuMO1P4t_sDHnSKPzkUb9D0DqunAmw_ko,131
|
| 61 |
+
pip/_internal/cli/__pycache__/__init__.cpython-314.pyc,,
|
| 62 |
+
pip/_internal/cli/__pycache__/autocompletion.cpython-314.pyc,,
|
| 63 |
+
pip/_internal/cli/__pycache__/base_command.cpython-314.pyc,,
|
| 64 |
+
pip/_internal/cli/__pycache__/cmdoptions.cpython-314.pyc,,
|
| 65 |
+
pip/_internal/cli/__pycache__/command_context.cpython-314.pyc,,
|
| 66 |
+
pip/_internal/cli/__pycache__/index_command.cpython-314.pyc,,
|
| 67 |
+
pip/_internal/cli/__pycache__/main.cpython-314.pyc,,
|
| 68 |
+
pip/_internal/cli/__pycache__/main_parser.cpython-314.pyc,,
|
| 69 |
+
pip/_internal/cli/__pycache__/parser.cpython-314.pyc,,
|
| 70 |
+
pip/_internal/cli/__pycache__/progress_bars.cpython-314.pyc,,
|
| 71 |
+
pip/_internal/cli/__pycache__/req_command.cpython-314.pyc,,
|
| 72 |
+
pip/_internal/cli/__pycache__/spinners.cpython-314.pyc,,
|
| 73 |
+
pip/_internal/cli/__pycache__/status_codes.cpython-314.pyc,,
|
| 74 |
+
pip/_internal/cli/autocompletion.py,sha256=gFtmiFhHccizyx4JPJnEKik-uftU7kwrUvm72cSFPHI,7353
|
| 75 |
+
pip/_internal/cli/base_command.py,sha256=yF5_mQEIrLG2G0nvymZS0nLhiDg4B0T89W5iX78H558,9431
|
| 76 |
+
pip/_internal/cli/cmdoptions.py,sha256=aBNxaIy4YV4AfjAqrG8RoZFcv7kwWzRAzg5mnZmOGDw,41180
|
| 77 |
+
pip/_internal/cli/command_context.py,sha256=kmu3EWZbfBega1oDamnGJTA_UaejhIQNuMj2CVmMXu0,817
|
| 78 |
+
pip/_internal/cli/index_command.py,sha256=_Qzc9oHnButVvD0wxYcHfe0uJZqRWOtIg5HaQpnSNeM,7504
|
| 79 |
+
pip/_internal/cli/main.py,sha256=O10guzBqF2MykAaZYo6sua097dXqC3nozKodtTOpHDc,3209
|
| 80 |
+
pip/_internal/cli/main_parser.py,sha256=7PhEfZ8AXiWUpTfB_KJMngI2sF8XIXl52alI1LZ_e_M,4368
|
| 81 |
+
pip/_internal/cli/parser.py,sha256=WvRHuI2MdFp187BWrY_D17L02asq9jmCnng81af52SQ,14147
|
| 82 |
+
pip/_internal/cli/progress_bars.py,sha256=13U3AY3DYwdEIwPYLtUn5B4QcpJUApLWCkfJ-u_PqzY,4706
|
| 83 |
+
pip/_internal/cli/req_command.py,sha256=wW-mRcPqPczeqxp9SQ_yh3YfrRAYAlEAZextlLcDbUc,18952
|
| 84 |
+
pip/_internal/cli/spinners.py,sha256=EJzZIZNyUtJljp3-WjcsyIrqxW-HUsfWzhuW84n_Tqw,7362
|
| 85 |
+
pip/_internal/cli/status_codes.py,sha256=9QhNe6hHqsiw5y5wufiNKBOgJjV0HF3I3P2K21nj-OM,136
|
| 86 |
+
pip/_internal/commands/__init__.py,sha256=aNeCbQurGWihfhQq7BqaLXHqWDQ0i3I04OS7kxK6plQ,4026
|
| 87 |
+
pip/_internal/commands/__pycache__/__init__.cpython-314.pyc,,
|
| 88 |
+
pip/_internal/commands/__pycache__/cache.cpython-314.pyc,,
|
| 89 |
+
pip/_internal/commands/__pycache__/check.cpython-314.pyc,,
|
| 90 |
+
pip/_internal/commands/__pycache__/completion.cpython-314.pyc,,
|
| 91 |
+
pip/_internal/commands/__pycache__/configuration.cpython-314.pyc,,
|
| 92 |
+
pip/_internal/commands/__pycache__/debug.cpython-314.pyc,,
|
| 93 |
+
pip/_internal/commands/__pycache__/download.cpython-314.pyc,,
|
| 94 |
+
pip/_internal/commands/__pycache__/freeze.cpython-314.pyc,,
|
| 95 |
+
pip/_internal/commands/__pycache__/hash.cpython-314.pyc,,
|
| 96 |
+
pip/_internal/commands/__pycache__/help.cpython-314.pyc,,
|
| 97 |
+
pip/_internal/commands/__pycache__/index.cpython-314.pyc,,
|
| 98 |
+
pip/_internal/commands/__pycache__/inspect.cpython-314.pyc,,
|
| 99 |
+
pip/_internal/commands/__pycache__/install.cpython-314.pyc,,
|
| 100 |
+
pip/_internal/commands/__pycache__/list.cpython-314.pyc,,
|
| 101 |
+
pip/_internal/commands/__pycache__/lock.cpython-314.pyc,,
|
| 102 |
+
pip/_internal/commands/__pycache__/search.cpython-314.pyc,,
|
| 103 |
+
pip/_internal/commands/__pycache__/show.cpython-314.pyc,,
|
| 104 |
+
pip/_internal/commands/__pycache__/uninstall.cpython-314.pyc,,
|
| 105 |
+
pip/_internal/commands/__pycache__/wheel.cpython-314.pyc,,
|
| 106 |
+
pip/_internal/commands/cache.py,sha256=YLZrLWNvChl0pImBkoSZGGD3fb2MfdYGpYF4J0aQs6w,9251
|
| 107 |
+
pip/_internal/commands/check.py,sha256=hVFBQezQ3zj4EydoWbFQj_afPUppMt7r9JPAlY22U6Y,2244
|
| 108 |
+
pip/_internal/commands/completion.py,sha256=LjvRIZ6QUiDXJL3IOMFeD-_J97HfjMGgEk0j2tWGu1U,4565
|
| 109 |
+
pip/_internal/commands/configuration.py,sha256=5M6KM30G3i6oMHAsVUSWP4E93cSSJQoJ6evURC754E8,10132
|
| 110 |
+
pip/_internal/commands/debug.py,sha256=tCWUOEyz2HdlLGTvzNqSZIAJQkQYCy58zBTdfIWyzSo,6556
|
| 111 |
+
pip/_internal/commands/download.py,sha256=cuXEj9PmoZ4kCsxm_BaZ_OkmIsV_gluLiOVSuOO2MS0,5404
|
| 112 |
+
pip/_internal/commands/freeze.py,sha256=Lg8EOokxnRZrwdLWJgLpBWxNUt7-HrkWlEC05e-vBww,3100
|
| 113 |
+
pip/_internal/commands/hash.py,sha256=GO9pRN3wXC2kQaovK57TaLYBMc3IltOH92O6QEw6YE0,1679
|
| 114 |
+
pip/_internal/commands/help.py,sha256=Bz3LcjNQXkz4Cu__pL4CZ86o4-HNLZj1NZWdlJhjuu0,1108
|
| 115 |
+
pip/_internal/commands/index.py,sha256=ny_hGzXuZHzjsq4CIJh_6YfZGVJm7G7LqQQdKTibJ_Q,5608
|
| 116 |
+
pip/_internal/commands/inspect.py,sha256=iiAM-JuL_Z1-QP34cgAloZPSr50Mc7wAvuFDRzHQrQk,3185
|
| 117 |
+
pip/_internal/commands/install.py,sha256=DREULA-TGbCKhqBBLkrbDKztyQ557QbDe6ZJUYuh2yk,33761
|
| 118 |
+
pip/_internal/commands/list.py,sha256=W-FyxXTrHJhqRl4ZoqFp68_5GAwC9GYuY6fhFTJdHzU,13908
|
| 119 |
+
pip/_internal/commands/lock.py,sha256=VhpNRWItLdqywSOIqXg3vuIyf59cozh09-Z6Z9cMiLM,5987
|
| 120 |
+
pip/_internal/commands/search.py,sha256=zbMsX_YASj6kXA6XIBgTDv0bGK51xG-CV3IynZJcE-c,5782
|
| 121 |
+
pip/_internal/commands/show.py,sha256=91_XljhCHWyCNfoFBa4XdacbumQgoNbhkcHHk_gcW9Y,8247
|
| 122 |
+
pip/_internal/commands/uninstall.py,sha256=CsOihqvb6ZA6O67L70oXeoLHeOfNzMM88H9g-9aocgw,3868
|
| 123 |
+
pip/_internal/commands/wheel.py,sha256=5m3NvVKatqLDOVoC_4WOFiDDrvQRjB52jkbvpbZApug,6187
|
| 124 |
+
pip/_internal/configuration.py,sha256=0eEdh1xTlOHhoCrTtwHYM5DEj7BbTmhIaCaNNj3srBM,14562
|
| 125 |
+
pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858
|
| 126 |
+
pip/_internal/distributions/__pycache__/__init__.cpython-314.pyc,,
|
| 127 |
+
pip/_internal/distributions/__pycache__/base.cpython-314.pyc,,
|
| 128 |
+
pip/_internal/distributions/__pycache__/installed.cpython-314.pyc,,
|
| 129 |
+
pip/_internal/distributions/__pycache__/sdist.cpython-314.pyc,,
|
| 130 |
+
pip/_internal/distributions/__pycache__/wheel.cpython-314.pyc,,
|
| 131 |
+
pip/_internal/distributions/base.py,sha256=BjYazg2EhbEgrO1W_JD6jEZIDVlUpA_WZkqU-NvCBkQ,1907
|
| 132 |
+
pip/_internal/distributions/installed.py,sha256=QPDhCqc-GTGiWZAAOFaVKMETg1m_kYuf6RBO9ZZlDqY,994
|
| 133 |
+
pip/_internal/distributions/sdist.py,sha256=hof85VbuXa_NydvFZj6zOtnxuPY15tsuCJVirLuNT-U,7783
|
| 134 |
+
pip/_internal/distributions/wheel.py,sha256=qOMVYzKT3tJsO0QundLjADcFiW_RODui4K51XRqMru8,1429
|
| 135 |
+
pip/_internal/exceptions.py,sha256=Zdu17pVbD1eI_yjg52Zd0ygAWDy8LtXS1Atfvn1pPts,38302
|
| 136 |
+
pip/_internal/index/__init__.py,sha256=tzwMH_fhQeubwMqHdSivasg1cRgTSbNg2CiMVnzMmyU,29
|
| 137 |
+
pip/_internal/index/__pycache__/__init__.cpython-314.pyc,,
|
| 138 |
+
pip/_internal/index/__pycache__/collector.cpython-314.pyc,,
|
| 139 |
+
pip/_internal/index/__pycache__/package_finder.cpython-314.pyc,,
|
| 140 |
+
pip/_internal/index/__pycache__/sources.cpython-314.pyc,,
|
| 141 |
+
pip/_internal/index/collector.py,sha256=bu-NBVToIeyYEbcv0_gtInAxsCK3E0yI6Je6yxAwE5k,16578
|
| 142 |
+
pip/_internal/index/package_finder.py,sha256=RoWK5rPkOcmOdiWEXhnk9aaFDgaj4QEE70FeQokqZf4,43149
|
| 143 |
+
pip/_internal/index/sources.py,sha256=fqqVli93Lr-HNk-wFD67yyfZzS6WeDkSZGv1RFw63QE,8621
|
| 144 |
+
pip/_internal/locations/__init__.py,sha256=iP9yVZn_4iPuNcaUNh2A_vJLcvUZ-8y4zvKLDVEqaM0,14022
|
| 145 |
+
pip/_internal/locations/__pycache__/__init__.cpython-314.pyc,,
|
| 146 |
+
pip/_internal/locations/__pycache__/_distutils.cpython-314.pyc,,
|
| 147 |
+
pip/_internal/locations/__pycache__/_sysconfig.cpython-314.pyc,,
|
| 148 |
+
pip/_internal/locations/__pycache__/base.cpython-314.pyc,,
|
| 149 |
+
pip/_internal/locations/_distutils.py,sha256=jpFj4V00rD9IR3vA9TqrGkwcdNVFc58LsChZavge9JY,5975
|
| 150 |
+
pip/_internal/locations/_sysconfig.py,sha256=HoZYQ1sVQD3IRpQCp4TpdTQLNrDFFzI9zRjXrTYnJWY,7788
|
| 151 |
+
pip/_internal/locations/base.py,sha256=5mVcXh9jnSepEO25aG2U-qZWguzeoOPIbT0mRiBDDME,2548
|
| 152 |
+
pip/_internal/main.py,sha256=1cHqjsfFCrMFf3B5twzocxTJUdHMLoXUpy5lJoFqUi8,338
|
| 153 |
+
pip/_internal/metadata/__init__.py,sha256=vp-JAxiWg_-l5F8AT0Jcey72uUnh8CDwwol9-KktHZ8,5824
|
| 154 |
+
pip/_internal/metadata/__pycache__/__init__.cpython-314.pyc,,
|
| 155 |
+
pip/_internal/metadata/__pycache__/_json.cpython-314.pyc,,
|
| 156 |
+
pip/_internal/metadata/__pycache__/base.cpython-314.pyc,,
|
| 157 |
+
pip/_internal/metadata/__pycache__/pkg_resources.cpython-314.pyc,,
|
| 158 |
+
pip/_internal/metadata/_json.py,sha256=hNvnMHOXLAyNlzirWhPL9Nx2CvCqa1iRma6Osq1YfV8,2711
|
| 159 |
+
pip/_internal/metadata/base.py,sha256=_o9lMyHmia4Ea9fhceyB9rp3jUUU8k7FmlhXB7N9_tM,25642
|
| 160 |
+
pip/_internal/metadata/importlib/__init__.py,sha256=jUUidoxnHcfITHHaAWG1G2i5fdBYklv_uJcjo2x7VYE,135
|
| 161 |
+
pip/_internal/metadata/importlib/__pycache__/__init__.cpython-314.pyc,,
|
| 162 |
+
pip/_internal/metadata/importlib/__pycache__/_compat.cpython-314.pyc,,
|
| 163 |
+
pip/_internal/metadata/importlib/__pycache__/_dists.cpython-314.pyc,,
|
| 164 |
+
pip/_internal/metadata/importlib/__pycache__/_envs.cpython-314.pyc,,
|
| 165 |
+
pip/_internal/metadata/importlib/_compat.py,sha256=sneVh4_6WxQZK4ljdl3ylVuP-q0ttSqbgl9mWt0HnOg,2804
|
| 166 |
+
pip/_internal/metadata/importlib/_dists.py,sha256=akX5xiLB_3dvbeb2gHL8i7wORi01_Cnty0HnKFQAF0Y,8717
|
| 167 |
+
pip/_internal/metadata/importlib/_envs.py,sha256=ulCRDEXYeinwS5AXL-Quj7C4Erk20kHhIwBmE2P3BYA,5590
|
| 168 |
+
pip/_internal/metadata/pkg_resources.py,sha256=NO76ZrfR2-LKJTyaXrmQoGhmJMArALvacrlZHViSDT8,10544
|
| 169 |
+
pip/_internal/models/__init__.py,sha256=AjmCEBxX_MH9f_jVjIGNCFJKYCYeSEe18yyvNx4uRKQ,62
|
| 170 |
+
pip/_internal/models/__pycache__/__init__.cpython-314.pyc,,
|
| 171 |
+
pip/_internal/models/__pycache__/candidate.cpython-314.pyc,,
|
| 172 |
+
pip/_internal/models/__pycache__/direct_url.cpython-314.pyc,,
|
| 173 |
+
pip/_internal/models/__pycache__/format_control.cpython-314.pyc,,
|
| 174 |
+
pip/_internal/models/__pycache__/index.cpython-314.pyc,,
|
| 175 |
+
pip/_internal/models/__pycache__/installation_report.cpython-314.pyc,,
|
| 176 |
+
pip/_internal/models/__pycache__/link.cpython-314.pyc,,
|
| 177 |
+
pip/_internal/models/__pycache__/release_control.cpython-314.pyc,,
|
| 178 |
+
pip/_internal/models/__pycache__/scheme.cpython-314.pyc,,
|
| 179 |
+
pip/_internal/models/__pycache__/search_scope.cpython-314.pyc,,
|
| 180 |
+
pip/_internal/models/__pycache__/selection_prefs.cpython-314.pyc,,
|
| 181 |
+
pip/_internal/models/__pycache__/target_python.cpython-314.pyc,,
|
| 182 |
+
pip/_internal/models/__pycache__/wheel.cpython-314.pyc,,
|
| 183 |
+
pip/_internal/models/candidate.py,sha256=MvANZJiHkw6Y4T-oYSM8710G6yYB4_DwofoD7BAmrnM,824
|
| 184 |
+
pip/_internal/models/direct_url.py,sha256=9RS3TQAXknwLd8JOOZWlbBV6WZFs9qn-YAYk0VQ8R_Q,944
|
| 185 |
+
pip/_internal/models/format_control.py,sha256=PwemYG1L27BM0f1KP61rm24wShENFyxqlD1TWu34alc,2471
|
| 186 |
+
pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030
|
| 187 |
+
pip/_internal/models/installation_report.py,sha256=U4MlXWFB-8ev_yheuMO9T2m_y5b4C-hOzVaoHVobl38,2846
|
| 188 |
+
pip/_internal/models/link.py,sha256=SSXtFO3YAreUYY2R0PqtKXbAe1vV9Bqt5r_qPN218zE,23474
|
| 189 |
+
pip/_internal/models/release_control.py,sha256=31Jh-ZHsTIBZLe-7uPNoYLiRSWXAjEI1jGNxwqLKd4A,3365
|
| 190 |
+
pip/_internal/models/scheme.py,sha256=G-O9KElabcXqbPwfE_66lzX5fGahh3Gu6DAkz_9ZhJw,558
|
| 191 |
+
pip/_internal/models/search_scope.py,sha256=_i-Gj_w_FwZAvTs7WhjslBDD70J-8hmIdjXByI8uEZQ,4461
|
| 192 |
+
pip/_internal/models/selection_prefs.py,sha256=0teekwSVxW5MOk0WPG5Novw5q_XJjxijcd75kZO6E9g,1503
|
| 193 |
+
pip/_internal/models/target_python.py,sha256=I0eFS-eia3kwhrOvgsphFZtNAB2IwXZ9Sr9fp6IjBP4,4243
|
| 194 |
+
pip/_internal/models/wheel.py,sha256=1SdfDvN7ALTsbyZ9EOsNy1GPirP1n6EjHyzPrZyLSh8,2920
|
| 195 |
+
pip/_internal/network/__init__.py,sha256=FMy06P__y6jMjUc8z3ZcQdKF-pmZ2zM14_vBeHPGhUI,49
|
| 196 |
+
pip/_internal/network/__pycache__/__init__.cpython-314.pyc,,
|
| 197 |
+
pip/_internal/network/__pycache__/auth.cpython-314.pyc,,
|
| 198 |
+
pip/_internal/network/__pycache__/cache.cpython-314.pyc,,
|
| 199 |
+
pip/_internal/network/__pycache__/download.cpython-314.pyc,,
|
| 200 |
+
pip/_internal/network/__pycache__/lazy_wheel.cpython-314.pyc,,
|
| 201 |
+
pip/_internal/network/__pycache__/session.cpython-314.pyc,,
|
| 202 |
+
pip/_internal/network/__pycache__/utils.cpython-314.pyc,,
|
| 203 |
+
pip/_internal/network/__pycache__/xmlrpc.cpython-314.pyc,,
|
| 204 |
+
pip/_internal/network/auth.py,sha256=TBpwe7zcaPuBdBldjAOZhf20lmkacpIgGA-AqLcr8eU,21545
|
| 205 |
+
pip/_internal/network/cache.py,sha256=6DlMXJ7pb35M1vgtTK0-L_kiHJFxf7t9zrfeJ9vqO34,4862
|
| 206 |
+
pip/_internal/network/download.py,sha256=SVrmKao567lfs8RaCdyuOitzIRCZp9Rf3RcDj_o8vkg,14618
|
| 207 |
+
pip/_internal/network/lazy_wheel.py,sha256=y9gVksdJCSjnLfYzs_m3DYUAtl3hc_k-xFPDBd9DgOs,7646
|
| 208 |
+
pip/_internal/network/session.py,sha256=2KqMv5Hu_PFRYRkDBhSjIbqbsBp0lXv4tqoLKnM2K6o,19924
|
| 209 |
+
pip/_internal/network/utils.py,sha256=OTA1vwFtu2Ee1zWrqLysBpRQGTjvXxCJJUTEqy7hNcA,8128
|
| 210 |
+
pip/_internal/network/xmlrpc.py,sha256=_-Rnk3vOff8uF9hAGmT6SLALflY1gMBcbGwS12fb_Y4,1830
|
| 211 |
+
pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 212 |
+
pip/_internal/operations/__pycache__/__init__.cpython-314.pyc,,
|
| 213 |
+
pip/_internal/operations/__pycache__/check.cpython-314.pyc,,
|
| 214 |
+
pip/_internal/operations/__pycache__/freeze.cpython-314.pyc,,
|
| 215 |
+
pip/_internal/operations/__pycache__/prepare.cpython-314.pyc,,
|
| 216 |
+
pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 217 |
+
pip/_internal/operations/build/__pycache__/__init__.cpython-314.pyc,,
|
| 218 |
+
pip/_internal/operations/build/__pycache__/build_tracker.cpython-314.pyc,,
|
| 219 |
+
pip/_internal/operations/build/__pycache__/metadata.cpython-314.pyc,,
|
| 220 |
+
pip/_internal/operations/build/__pycache__/metadata_editable.cpython-314.pyc,,
|
| 221 |
+
pip/_internal/operations/build/__pycache__/wheel.cpython-314.pyc,,
|
| 222 |
+
pip/_internal/operations/build/__pycache__/wheel_editable.cpython-314.pyc,,
|
| 223 |
+
pip/_internal/operations/build/build_tracker.py,sha256=W3b5cmkMWPaE6QIwfzsTayJo7-OlxFHWDxfPuax1KcE,4771
|
| 224 |
+
pip/_internal/operations/build/metadata.py,sha256=INHaeiRfOiLYCXApfDNRo9Cw2xI4VwTc0KItvfdfOjk,1421
|
| 225 |
+
pip/_internal/operations/build/metadata_editable.py,sha256=oWudMsnjy4loO_Jy7g4N9nxsnaEX_iDlVRgCy7pu1rs,1509
|
| 226 |
+
pip/_internal/operations/build/wheel.py,sha256=3bP-nNiJ4S8JvMaBnyessXQUBhxTqt1GBx6DQ1iPJDY,1136
|
| 227 |
+
pip/_internal/operations/build/wheel_editable.py,sha256=q3kfElclM6FutVbFwE87JOTpVWt5ixDf3_UkHAIVfz4,1478
|
| 228 |
+
pip/_internal/operations/check.py,sha256=k7mTjLb5yZbidYw8Nt9vu7_u1L1ekLOzUECeN4qcNsA,5891
|
| 229 |
+
pip/_internal/operations/freeze.py,sha256=PDdY-y_ZtZZJLAKcaWPIGRKAGW7DXR48f0aMRU0j7BA,9854
|
| 230 |
+
pip/_internal/operations/install/__init__.py,sha256=ak-UETcQPKlFZaWoYKWu5QVXbpFBvg0sXc3i0O4vSYY,50
|
| 231 |
+
pip/_internal/operations/install/__pycache__/__init__.cpython-314.pyc,,
|
| 232 |
+
pip/_internal/operations/install/__pycache__/wheel.cpython-314.pyc,,
|
| 233 |
+
pip/_internal/operations/install/wheel.py,sha256=qhFwJSShLm4quY8zIss_t-bff0YV49HTNAKfu4fSpic,29078
|
| 234 |
+
pip/_internal/operations/prepare.py,sha256=zLkVMSDu9cUR0KnDm3bZ3QvIeaErhJKrr109UyDMlKQ,35030
|
| 235 |
+
pip/_internal/pyproject.py,sha256=J-sTWqC-XfsKQgz9m1bypMWZPHItsSHzIN_NWeIRmhM,4555
|
| 236 |
+
pip/_internal/req/__init__.py,sha256=x3uB7ZW-l_MOPm8ldF8Gi0dh5C8W23dB_wOH2NmWpFc,3140
|
| 237 |
+
pip/_internal/req/__pycache__/__init__.cpython-314.pyc,,
|
| 238 |
+
pip/_internal/req/__pycache__/constructors.cpython-314.pyc,,
|
| 239 |
+
pip/_internal/req/__pycache__/pep723.cpython-314.pyc,,
|
| 240 |
+
pip/_internal/req/__pycache__/req_dependency_group.cpython-314.pyc,,
|
| 241 |
+
pip/_internal/req/__pycache__/req_file.cpython-314.pyc,,
|
| 242 |
+
pip/_internal/req/__pycache__/req_install.cpython-314.pyc,,
|
| 243 |
+
pip/_internal/req/__pycache__/req_set.cpython-314.pyc,,
|
| 244 |
+
pip/_internal/req/__pycache__/req_uninstall.cpython-314.pyc,,
|
| 245 |
+
pip/_internal/req/constructors.py,sha256=fwILg3OiIK_f13RUGX5zQyz4YasUMp7MC6u9uPvoGzw,22276
|
| 246 |
+
pip/_internal/req/pep723.py,sha256=JsG1p3CaVcW8cjclD2kDj7d9qtVrNQaevY_UnKh00tk,1242
|
| 247 |
+
pip/_internal/req/req_dependency_group.py,sha256=PrWKtlwI8xbWnWEKjXs9RkrpQx2_h02ABefYT0ZdeQg,3145
|
| 248 |
+
pip/_internal/req/req_file.py,sha256=dmGBSxZ9dj1eOq2NXv94bor0u1n8P4-AvmJL-vYzIsE,20644
|
| 249 |
+
pip/_internal/req/req_install.py,sha256=r289__WNVaTxpRtLFUz3FpNvdGv8yYa87G-QkP5DsJc,32173
|
| 250 |
+
pip/_internal/req/req_set.py,sha256=awkqIXnYA4Prmsj0Qb3zhqdbYUmXd-1o0P-KZ3mvRQs,2828
|
| 251 |
+
pip/_internal/req/req_uninstall.py,sha256=t5HSYuLoLxnF7jrcIBDjTEP-sjjr8O4fZouEqOkVM_c,24276
|
| 252 |
+
pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 253 |
+
pip/_internal/resolution/__pycache__/__init__.cpython-314.pyc,,
|
| 254 |
+
pip/_internal/resolution/__pycache__/base.cpython-314.pyc,,
|
| 255 |
+
pip/_internal/resolution/base.py,sha256=J9Fq3kFOjFj9gBShYQtldEFPrRGSWtsp-rO9rIc4W4g,573
|
| 256 |
+
pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 257 |
+
pip/_internal/resolution/legacy/__pycache__/__init__.cpython-314.pyc,,
|
| 258 |
+
pip/_internal/resolution/legacy/__pycache__/resolver.cpython-314.pyc,,
|
| 259 |
+
pip/_internal/resolution/legacy/resolver.py,sha256=2CVLIZLm_nXkfJTt5RKtIbIxvCkn9Gavf4Yp9c1IjDU,24110
|
| 260 |
+
pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 261 |
+
pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-314.pyc,,
|
| 262 |
+
pip/_internal/resolution/resolvelib/__pycache__/base.cpython-314.pyc,,
|
| 263 |
+
pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-314.pyc,,
|
| 264 |
+
pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-314.pyc,,
|
| 265 |
+
pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-314.pyc,,
|
| 266 |
+
pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-314.pyc,,
|
| 267 |
+
pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-314.pyc,,
|
| 268 |
+
pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-314.pyc,,
|
| 269 |
+
pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-314.pyc,,
|
| 270 |
+
pip/_internal/resolution/resolvelib/base.py,sha256=fo7ojoQtgjmQH5ca3VyKJAO3p5sHnihMDw79Z8_31-U,5900
|
| 271 |
+
pip/_internal/resolution/resolvelib/candidates.py,sha256=SEkpEH6T6zl7Ve7Dw9oIS6tnREj-1U_q4U-2xEH0ISc,20848
|
| 272 |
+
pip/_internal/resolution/resolvelib/factory.py,sha256=rqwnD4GWEdVgG7nGP6OGj493yiGW6jZr0cW_msMOK_U,36162
|
| 273 |
+
pip/_internal/resolution/resolvelib/found_candidates.py,sha256=F55YtXekKUbVwphlXska01LkdTGVgx1qONlLK7BX-HM,6005
|
| 274 |
+
pip/_internal/resolution/resolvelib/provider.py,sha256=Nkx4A16MOAxjCj9jFm8wmlMk5cbWh1YK2-UYWTKEJCw,12310
|
| 275 |
+
pip/_internal/resolution/resolvelib/reporter.py,sha256=tEC7MF8IqGU4Ww9t61YVfNFKtoaQpb6-AwLZptJz1VE,3918
|
| 276 |
+
pip/_internal/resolution/resolvelib/requirements.py,sha256=Izl9n8nc188lA1BSPS8QxfudfDQPHgngw-ij6hXt0nQ,8239
|
| 277 |
+
pip/_internal/resolution/resolvelib/resolver.py,sha256=wM3CcRTYPdjnQ1ai3YP6htFmkE32o1xx2RgjU-ykAjc,13849
|
| 278 |
+
pip/_internal/self_outdated_check.py,sha256=9XxOXPqsZlKceTP2tZS7I7rXjodnqd3GrIQ8U0_L8BM,8097
|
| 279 |
+
pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 280 |
+
pip/_internal/utils/__pycache__/__init__.cpython-314.pyc,,
|
| 281 |
+
pip/_internal/utils/__pycache__/_jaraco_text.cpython-314.pyc,,
|
| 282 |
+
pip/_internal/utils/__pycache__/_log.cpython-314.pyc,,
|
| 283 |
+
pip/_internal/utils/__pycache__/appdirs.cpython-314.pyc,,
|
| 284 |
+
pip/_internal/utils/__pycache__/compat.cpython-314.pyc,,
|
| 285 |
+
pip/_internal/utils/__pycache__/compatibility_tags.cpython-314.pyc,,
|
| 286 |
+
pip/_internal/utils/__pycache__/datetime.cpython-314.pyc,,
|
| 287 |
+
pip/_internal/utils/__pycache__/deprecation.cpython-314.pyc,,
|
| 288 |
+
pip/_internal/utils/__pycache__/direct_url_helpers.cpython-314.pyc,,
|
| 289 |
+
pip/_internal/utils/__pycache__/egg_link.cpython-314.pyc,,
|
| 290 |
+
pip/_internal/utils/__pycache__/entrypoints.cpython-314.pyc,,
|
| 291 |
+
pip/_internal/utils/__pycache__/filesystem.cpython-314.pyc,,
|
| 292 |
+
pip/_internal/utils/__pycache__/filetypes.cpython-314.pyc,,
|
| 293 |
+
pip/_internal/utils/__pycache__/glibc.cpython-314.pyc,,
|
| 294 |
+
pip/_internal/utils/__pycache__/hashes.cpython-314.pyc,,
|
| 295 |
+
pip/_internal/utils/__pycache__/logging.cpython-314.pyc,,
|
| 296 |
+
pip/_internal/utils/__pycache__/misc.cpython-314.pyc,,
|
| 297 |
+
pip/_internal/utils/__pycache__/packaging.cpython-314.pyc,,
|
| 298 |
+
pip/_internal/utils/__pycache__/pylock.cpython-314.pyc,,
|
| 299 |
+
pip/_internal/utils/__pycache__/retry.cpython-314.pyc,,
|
| 300 |
+
pip/_internal/utils/__pycache__/subprocess.cpython-314.pyc,,
|
| 301 |
+
pip/_internal/utils/__pycache__/temp_dir.cpython-314.pyc,,
|
| 302 |
+
pip/_internal/utils/__pycache__/unpacking.cpython-314.pyc,,
|
| 303 |
+
pip/_internal/utils/__pycache__/urls.cpython-314.pyc,,
|
| 304 |
+
pip/_internal/utils/__pycache__/virtualenv.cpython-314.pyc,,
|
| 305 |
+
pip/_internal/utils/__pycache__/wheel.cpython-314.pyc,,
|
| 306 |
+
pip/_internal/utils/_jaraco_text.py,sha256=M15uUPIh5NpP1tdUGBxRau6q1ZAEtI8-XyLEETscFfE,3350
|
| 307 |
+
pip/_internal/utils/_log.py,sha256=-jHLOE_THaZz5BFcCnoSL9EYAtJ0nXem49s9of4jvKw,1015
|
| 308 |
+
pip/_internal/utils/appdirs.py,sha256=LrzDPZMKVh0rubtCx9vu3XlZbLCSug6VSj4Qsvt66BA,1681
|
| 309 |
+
pip/_internal/utils/compat.py,sha256=i2JUZXGrqP-ZoD8t4gwckBA3KYNUWCluvRDXScts5fE,2601
|
| 310 |
+
pip/_internal/utils/compatibility_tags.py,sha256=DiNSLqpuruXUamGQwOJ2WZByDGLTGaXi9O-Xf8fOi34,6630
|
| 311 |
+
pip/_internal/utils/datetime.py,sha256=kuJOf1mW8G5tRFN6jWardddS-9qSaR53lK1jmx3NTZY,868
|
| 312 |
+
pip/_internal/utils/deprecation.py,sha256=IQFumZa-XWJ3UoPnX4duwbf79CTzOZD_Vk1PkivAu4g,4537
|
| 313 |
+
pip/_internal/utils/direct_url_helpers.py,sha256=WCCPJnmoPHz7kiYePrEcVAen2lDpCT9WQVImoHHWCO8,3363
|
| 314 |
+
pip/_internal/utils/egg_link.py,sha256=YWfsrbmfcrfWgqQYy6OuIjsyb9IfL1q_2v4zsms1WjI,2459
|
| 315 |
+
pip/_internal/utils/entrypoints.py,sha256=uPjAyShKObdotjQjJUzprQ6r3xQvDIZwUYfHHqZ7Dok,3324
|
| 316 |
+
pip/_internal/utils/filesystem.py,sha256=gig73bOOS1NnDB94EDDvdDnHjQoeRz_TlfQPiCV7Me0,6812
|
| 317 |
+
pip/_internal/utils/filetypes.py,sha256=sEMa38qaqjvx1Zid3OCAUja31BOBU-USuSMPBvU3yjo,689
|
| 318 |
+
pip/_internal/utils/glibc.py,sha256=sEh8RJJLYSdRvTqAO4THVPPA-YSDVLD4SI9So-bxX1U,3726
|
| 319 |
+
pip/_internal/utils/hashes.py,sha256=38-bCOJSHippQ7r9RttrMHxb2mv3EARt1Gw8kFmW73g,5040
|
| 320 |
+
pip/_internal/utils/logging.py,sha256=hGTjBuIjNZxydV6vs4woxAbhkHI8L-9uCO14OCvPlA8,13919
|
| 321 |
+
pip/_internal/utils/misc.py,sha256=28LWQFvFWmrpJqMcV0soif1xnl3_gb25B3gG_iU5zhM,25408
|
| 322 |
+
pip/_internal/utils/packaging.py,sha256=s5tpUmFumwV0H9JSTzryrIY4JwQM8paGt7Sm7eNwt2Y,1601
|
| 323 |
+
pip/_internal/utils/pylock.py,sha256=XQTZ1ggh7yfpHfRdbvjSAe1peOtg2Z2xFrUeZ7wl4Ts,10290
|
| 324 |
+
pip/_internal/utils/retry.py,sha256=oOK3VGgKRKxSkXDPARf73cnQS3hDtnE9-feAg4AlNZE,1488
|
| 325 |
+
pip/_internal/utils/subprocess.py,sha256=nqNz2p3G4QANCCBcaJkqIQvQUYA50TwjZnoYjiyPxs8,9001
|
| 326 |
+
pip/_internal/utils/temp_dir.py,sha256=TDiL9BzEp2eHalQLH0rhyM3m2xuydEg9c6t1h4bT5l8,9303
|
| 327 |
+
pip/_internal/utils/unpacking.py,sha256=5i9Sn__SNElhoCXnLt2SvQaQ-SnTzcUaDH1_JI1THUw,14789
|
| 328 |
+
pip/_internal/utils/urls.py,sha256=JQWrRAoXHBpPsY9Aij61bsZjMQLCcuyrUO1R6W_BwIo,1647
|
| 329 |
+
pip/_internal/utils/virtualenv.py,sha256=9od4LsceDynUSsLTEXXgtumlUQ8VvKp86b2xzK0Yu3g,2272
|
| 330 |
+
pip/_internal/utils/wheel.py,sha256=YdRuj6MicG-Q9Mg03FbUv1WTLam6Lc7AgijY4voVyis,4468
|
| 331 |
+
pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596
|
| 332 |
+
pip/_internal/vcs/__pycache__/__init__.cpython-314.pyc,,
|
| 333 |
+
pip/_internal/vcs/__pycache__/bazaar.cpython-314.pyc,,
|
| 334 |
+
pip/_internal/vcs/__pycache__/git.cpython-314.pyc,,
|
| 335 |
+
pip/_internal/vcs/__pycache__/mercurial.cpython-314.pyc,,
|
| 336 |
+
pip/_internal/vcs/__pycache__/subversion.cpython-314.pyc,,
|
| 337 |
+
pip/_internal/vcs/__pycache__/versioncontrol.cpython-314.pyc,,
|
| 338 |
+
pip/_internal/vcs/bazaar.py,sha256=3W1eHjkYx2vc6boeb2NBh4I_rlGAXM-vrzfNhLm1Rxg,3734
|
| 339 |
+
pip/_internal/vcs/git.py,sha256=L52rqJN--Kq0kjAR8_9Si8RJEcNlULJdk6Vipg8ZlfA,19273
|
| 340 |
+
pip/_internal/vcs/mercurial.py,sha256=w1ZJWLKqNP1onEjkfjlwBVnMqPZNSIER8ayjQcnTq4w,5575
|
| 341 |
+
pip/_internal/vcs/subversion.py,sha256=uUgdPvxmvEB8Qwtjr0Hc0XgFjbiNi5cbvI4vARLOJXo,11787
|
| 342 |
+
pip/_internal/vcs/versioncontrol.py,sha256=JsUgTG53DkJPy5mXVBH7uqeFQyaL69707ibts5uCBYA,22579
|
| 343 |
+
pip/_internal/wheel_builder.py,sha256=kdPvAcxfknA8B_4g6hPDjB7C4uzL_cV5Vde2Oj2Ipzk,9146
|
| 344 |
+
pip/_vendor/README.rst,sha256=t7IinjaiuwUh812XmVApQHJb8pTw33U8A9URqy6GlF4,9222
|
| 345 |
+
pip/_vendor/__init__.py,sha256=WzusPTGWIMeQQWSVJ0h2rafGkVTa9WKJ2HT-2-EoZrU,4907
|
| 346 |
+
pip/_vendor/__pycache__/__init__.cpython-314.pyc,,
|
| 347 |
+
pip/_vendor/bom.cdx.json,sha256=eX4gCqf_8W-tn7Le6uzlCisr7GI-tsN1kXbu5s7hkyg,5319
|
| 348 |
+
pip/_vendor/cachecontrol/LICENSE.txt,sha256=hu7uh74qQ_P_H1ZJb0UfaSQ5JvAl_tuwM2ZsMExMFhs,558
|
| 349 |
+
pip/_vendor/cachecontrol/__init__.py,sha256=GxwRkm_TQBtPZpfpVK9r6S9dAy2DVnVgDVHJKTiPZ1k,820
|
| 350 |
+
pip/_vendor/cachecontrol/__pycache__/__init__.cpython-314.pyc,,
|
| 351 |
+
pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-314.pyc,,
|
| 352 |
+
pip/_vendor/cachecontrol/__pycache__/adapter.cpython-314.pyc,,
|
| 353 |
+
pip/_vendor/cachecontrol/__pycache__/cache.cpython-314.pyc,,
|
| 354 |
+
pip/_vendor/cachecontrol/__pycache__/controller.cpython-314.pyc,,
|
| 355 |
+
pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-314.pyc,,
|
| 356 |
+
pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-314.pyc,,
|
| 357 |
+
pip/_vendor/cachecontrol/__pycache__/serialize.cpython-314.pyc,,
|
| 358 |
+
pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-314.pyc,,
|
| 359 |
+
pip/_vendor/cachecontrol/_cmd.py,sha256=iist2EpzJvDVIhMAxXq8iFnTBsiZAd6iplxfmNboNyk,1737
|
| 360 |
+
pip/_vendor/cachecontrol/adapter.py,sha256=W-HW-l01gyCsnxkOyCbqx7sxrWYoBbKrDsKkVVQN6NE,6586
|
| 361 |
+
pip/_vendor/cachecontrol/cache.py,sha256=OXwv7Fn2AwnKNiahJHnjtvaKLndvVLv_-zO-ltlV9qI,1953
|
| 362 |
+
pip/_vendor/cachecontrol/caches/__init__.py,sha256=dtrrroK5BnADR1GWjCZ19aZ0tFsMfvFBtLQQU1sp_ag,303
|
| 363 |
+
pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-314.pyc,,
|
| 364 |
+
pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-314.pyc,,
|
| 365 |
+
pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-314.pyc,,
|
| 366 |
+
pip/_vendor/cachecontrol/caches/file_cache.py,sha256=d8upFmy_zwaCmlbWEVBlLXFddt8Zw8c5SFpxeOZsdfw,4117
|
| 367 |
+
pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=9rmqwtYu_ljVkW6_oLqbC7EaX_a8YT_yLuna-eS0dgo,1386
|
| 368 |
+
pip/_vendor/cachecontrol/controller.py,sha256=xBauC-vUSu5GsJsxD4-W-JaKqqbBz0MN6Zv8PA2N8hI,19102
|
| 369 |
+
pip/_vendor/cachecontrol/filewrapper.py,sha256=DhxC_rSk-beKdbsYhfvBUDovQHX9r3gHH_jP9-q_mKk,4354
|
| 370 |
+
pip/_vendor/cachecontrol/heuristics.py,sha256=gqMXU8w0gQuEQiSdu3Yg-0vd9kW7nrWKbLca75rheGE,4881
|
| 371 |
+
pip/_vendor/cachecontrol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 372 |
+
pip/_vendor/cachecontrol/serialize.py,sha256=HQd2IllQ05HzPkVLMXTF2uX5mjEQjDBkxCqUJUODpZk,5163
|
| 373 |
+
pip/_vendor/cachecontrol/wrapper.py,sha256=hsGc7g8QGQTT-4f8tgz3AM5qwScg6FO0BSdLSRdEvpU,1417
|
| 374 |
+
pip/_vendor/certifi/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
|
| 375 |
+
pip/_vendor/certifi/__init__.py,sha256=-W1R_y8WCaSkT1tdjuxH_zTBZY1YH6xQgdN1nbBajOE,94
|
| 376 |
+
pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255
|
| 377 |
+
pip/_vendor/certifi/__pycache__/__init__.cpython-314.pyc,,
|
| 378 |
+
pip/_vendor/certifi/__pycache__/__main__.cpython-314.pyc,,
|
| 379 |
+
pip/_vendor/certifi/__pycache__/core.cpython-314.pyc,,
|
| 380 |
+
pip/_vendor/certifi/cacert.pem,sha256=u8fpwB11UbuKFZtd7dmJuO484QWv9SK2jrGwG_hUyrA,234354
|
| 381 |
+
pip/_vendor/certifi/core.py,sha256=gu_ECVI1m3Rq0ytpsNE61hgQGcKaOAt9Rs9G8KsTCOI,3442
|
| 382 |
+
pip/_vendor/certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 383 |
+
pip/_vendor/distlib/LICENSE.txt,sha256=gI4QyKarjesUn_mz-xn0R6gICUYG1xKpylf-rTVSWZ0,14531
|
| 384 |
+
pip/_vendor/distlib/__init__.py,sha256=tmYThB1xmgfRjRtyy8YP0i0ymrF2n6eVswcwK6OYbcc,625
|
| 385 |
+
pip/_vendor/distlib/__pycache__/__init__.cpython-314.pyc,,
|
| 386 |
+
pip/_vendor/distlib/__pycache__/compat.cpython-314.pyc,,
|
| 387 |
+
pip/_vendor/distlib/__pycache__/resources.cpython-314.pyc,,
|
| 388 |
+
pip/_vendor/distlib/__pycache__/scripts.cpython-314.pyc,,
|
| 389 |
+
pip/_vendor/distlib/__pycache__/util.cpython-314.pyc,,
|
| 390 |
+
pip/_vendor/distlib/compat.py,sha256=HTzhOY98c_fyMVE7xtVuZZ-AVkN4-RGes-hWmvuwquk,40605
|
| 391 |
+
pip/_vendor/distlib/resources.py,sha256=wZt_Ako8TY-L6ZIAmP42J7MNX0XTEF6gkM0Om5F7UPY,11350
|
| 392 |
+
pip/_vendor/distlib/scripts.py,sha256=rVm4A-YfGQDpsORwOy7AwepyRTayuy6rc-MnaCMBAcM,18835
|
| 393 |
+
pip/_vendor/distlib/t32.exe,sha256=a0GV5kCoWsMutvliiCKmIgV98eRZ33wXoS-XrqvJQVs,97792
|
| 394 |
+
pip/_vendor/distlib/t64-arm.exe,sha256=68TAa32V504xVBnufojh0PcenpR3U4wAqTqf-MZqbPw,182784
|
| 395 |
+
pip/_vendor/distlib/t64.exe,sha256=gaYY8hy4fbkHYTTnA4i26ct8IQZzkBG2pRdy0iyuBrc,108032
|
| 396 |
+
pip/_vendor/distlib/util.py,sha256=n2QDXvB0XtwrtehygnwdLgLkLia58sZjzgZPKyD6rAE,68167
|
| 397 |
+
pip/_vendor/distlib/w32.exe,sha256=R4csx3-OGM9kL4aPIzQKRo5TfmRSHZo6QWyLhDhNBks,91648
|
| 398 |
+
pip/_vendor/distlib/w64-arm.exe,sha256=xdyYhKj0WDcVUOCb05blQYvzdYIKMbmJn2SZvzkcey4,168448
|
| 399 |
+
pip/_vendor/distlib/w64.exe,sha256=ejGf-rojoBfXseGLpya6bFTFPWRG21X5KvU8J5iU-K0,101888
|
| 400 |
+
pip/_vendor/distro/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
|
| 401 |
+
pip/_vendor/distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981
|
| 402 |
+
pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64
|
| 403 |
+
pip/_vendor/distro/__pycache__/__init__.cpython-314.pyc,,
|
| 404 |
+
pip/_vendor/distro/__pycache__/__main__.cpython-314.pyc,,
|
| 405 |
+
pip/_vendor/distro/__pycache__/distro.cpython-314.pyc,,
|
| 406 |
+
pip/_vendor/distro/distro.py,sha256=XqbefacAhDT4zr_trnbA15eY8vdK4GTghgmvUGrEM_4,49430
|
| 407 |
+
pip/_vendor/distro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 408 |
+
pip/_vendor/idna/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
|
| 409 |
+
pip/_vendor/idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868
|
| 410 |
+
pip/_vendor/idna/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83
|
| 411 |
+
pip/_vendor/idna/__pycache__/__init__.cpython-314.pyc,,
|
| 412 |
+
pip/_vendor/idna/__pycache__/__main__.cpython-314.pyc,,
|
| 413 |
+
pip/_vendor/idna/__pycache__/cli.cpython-314.pyc,,
|
| 414 |
+
pip/_vendor/idna/__pycache__/codec.cpython-314.pyc,,
|
| 415 |
+
pip/_vendor/idna/__pycache__/compat.cpython-314.pyc,,
|
| 416 |
+
pip/_vendor/idna/__pycache__/core.cpython-314.pyc,,
|
| 417 |
+
pip/_vendor/idna/__pycache__/idnadata.cpython-314.pyc,,
|
| 418 |
+
pip/_vendor/idna/__pycache__/intranges.cpython-314.pyc,,
|
| 419 |
+
pip/_vendor/idna/__pycache__/package_data.cpython-314.pyc,,
|
| 420 |
+
pip/_vendor/idna/__pycache__/uts46data.cpython-314.pyc,,
|
| 421 |
+
pip/_vendor/idna/cli.py,sha256=swqJLMNc8Uzs60KziNpbWnHuqlG3WRQwJSbo4n8xDAo,4139
|
| 422 |
+
pip/_vendor/idna/codec.py,sha256=JRbo-f7pEkLdWeiH89Z72UR4VBYhvKDFrQBeNX6sRDE,5040
|
| 423 |
+
pip/_vendor/idna/compat.py,sha256=AepA39ceRHxkfHP41-FvKW5Ki-f4PfUZ90RUMlCNdmo,1353
|
| 424 |
+
pip/_vendor/idna/core.py,sha256=SfOr1xO3PoE0RDYx7bMciAnjiyjJPbPw_93AB5IUYOw,24685
|
| 425 |
+
pip/_vendor/idna/idnadata.py,sha256=Af-mo8WBmkhAK6TyXKOQH88OX0mQNDKtdL7UWtQpppk,44862
|
| 426 |
+
pip/_vendor/idna/intranges.py,sha256=g49scLSkqJtAhLmOODa7hVHriSjmb60tiTsEoocJdBI,1851
|
| 427 |
+
pip/_vendor/idna/package_data.py,sha256=TeI94EqAFAFaXfBJwsOPUMLn2969uirPa-DaeaceAyU,21
|
| 428 |
+
pip/_vendor/idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 429 |
+
pip/_vendor/idna/uts46data.py,sha256=jujNz5QqWMcJf-XYLv4X1jBvb5FlI0t6-e1mILsgbPk,234325
|
| 430 |
+
pip/_vendor/msgpack/COPYING,sha256=SS3tuoXaWHL3jmCRvNH-pHTWYNNay03ulkuKqz8AdCc,614
|
| 431 |
+
pip/_vendor/msgpack/__init__.py,sha256=RA8gcqK17YpkxBnNwXJVa1oa2LygWDgfF1nA1NPw3mo,1109
|
| 432 |
+
pip/_vendor/msgpack/__pycache__/__init__.cpython-314.pyc,,
|
| 433 |
+
pip/_vendor/msgpack/__pycache__/exceptions.cpython-314.pyc,,
|
| 434 |
+
pip/_vendor/msgpack/__pycache__/ext.cpython-314.pyc,,
|
| 435 |
+
pip/_vendor/msgpack/__pycache__/fallback.cpython-314.pyc,,
|
| 436 |
+
pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081
|
| 437 |
+
pip/_vendor/msgpack/ext.py,sha256=kteJv03n9tYzd5oo3xYopVTo4vRaAxonBQQJhXohZZo,5726
|
| 438 |
+
pip/_vendor/msgpack/fallback.py,sha256=0g1Pzp0vtmBEmJ5w9F3s_-JMVURP8RS4G1cc5TRaAsI,32390
|
| 439 |
+
pip/_vendor/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
|
| 440 |
+
pip/_vendor/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
|
| 441 |
+
pip/_vendor/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
|
| 442 |
+
pip/_vendor/packaging/__init__.py,sha256=QhMEdPu2XogrJzV3S0KWS6t7l0I9k8EeDRJl4fnw87s,494
|
| 443 |
+
pip/_vendor/packaging/__pycache__/__init__.cpython-314.pyc,,
|
| 444 |
+
pip/_vendor/packaging/__pycache__/_elffile.cpython-314.pyc,,
|
| 445 |
+
pip/_vendor/packaging/__pycache__/_manylinux.cpython-314.pyc,,
|
| 446 |
+
pip/_vendor/packaging/__pycache__/_musllinux.cpython-314.pyc,,
|
| 447 |
+
pip/_vendor/packaging/__pycache__/_parser.cpython-314.pyc,,
|
| 448 |
+
pip/_vendor/packaging/__pycache__/_structures.cpython-314.pyc,,
|
| 449 |
+
pip/_vendor/packaging/__pycache__/_tokenizer.cpython-314.pyc,,
|
| 450 |
+
pip/_vendor/packaging/__pycache__/dependency_groups.cpython-314.pyc,,
|
| 451 |
+
pip/_vendor/packaging/__pycache__/direct_url.cpython-314.pyc,,
|
| 452 |
+
pip/_vendor/packaging/__pycache__/errors.cpython-314.pyc,,
|
| 453 |
+
pip/_vendor/packaging/__pycache__/markers.cpython-314.pyc,,
|
| 454 |
+
pip/_vendor/packaging/__pycache__/metadata.cpython-314.pyc,,
|
| 455 |
+
pip/_vendor/packaging/__pycache__/pylock.cpython-314.pyc,,
|
| 456 |
+
pip/_vendor/packaging/__pycache__/requirements.cpython-314.pyc,,
|
| 457 |
+
pip/_vendor/packaging/__pycache__/specifiers.cpython-314.pyc,,
|
| 458 |
+
pip/_vendor/packaging/__pycache__/tags.cpython-314.pyc,,
|
| 459 |
+
pip/_vendor/packaging/__pycache__/utils.cpython-314.pyc,,
|
| 460 |
+
pip/_vendor/packaging/__pycache__/version.cpython-314.pyc,,
|
| 461 |
+
pip/_vendor/packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211
|
| 462 |
+
pip/_vendor/packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559
|
| 463 |
+
pip/_vendor/packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707
|
| 464 |
+
pip/_vendor/packaging/_parser.py,sha256=Kf2nsDw4c54X82pY8ba4F02Bve6OygGMAjL-Begqcew,11698
|
| 465 |
+
pip/_vendor/packaging/_structures.py,sha256=60jRbF78p8z5MKnNd6cAprgOadCJHV0DlmUmRBqFZcs,1109
|
| 466 |
+
pip/_vendor/packaging/_tokenizer.py,sha256=tFU2Wr-ZZJdAbkXLEJo7qUQDJaIkfft9DqaifiEND7A,5391
|
| 467 |
+
pip/_vendor/packaging/dependency_groups.py,sha256=XZIAVFK9uHG4RCGprmJn3VInUWMesxha_kytJuMO9eY,10218
|
| 468 |
+
pip/_vendor/packaging/direct_url.py,sha256=eKmbDiPP1sLV4Mj_kCSZqqknrIyVO9Sr7JpF8KCjp4U,10917
|
| 469 |
+
pip/_vendor/packaging/errors.py,sha256=6hfEYXAf8v_IF65-lFadJOMIieBP2xIKtyEXjG1nGIs,2680
|
| 470 |
+
pip/_vendor/packaging/licenses/__init__.py,sha256=_Jx0XRiD_58palsWnyLrLuh59ZpGCPIPXLKdZo9OJvQ,7293
|
| 471 |
+
pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-314.pyc,,
|
| 472 |
+
pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-314.pyc,,
|
| 473 |
+
pip/_vendor/packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122
|
| 474 |
+
pip/_vendor/packaging/markers.py,sha256=QixBVcb9D2HjwEYiuhpNkbqk9znPRbU8zNX0kR1RIrU,17067
|
| 475 |
+
pip/_vendor/packaging/metadata.py,sha256=crAh0E3GVGVqPlu6EdRFsaG-Y6UYznTUqjuGKRGPv6c,38770
|
| 476 |
+
pip/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 477 |
+
pip/_vendor/packaging/pylock.py,sha256=G_1gncTmDbRLY1jo4VDI9Uw-b5IErh_Q9V_BbVJTmD8,33890
|
| 478 |
+
pip/_vendor/packaging/requirements.py,sha256=Q-BdEHVW5K785GBXt7RcP4UEsdIKWoWFqlrHjR4WV50,4395
|
| 479 |
+
pip/_vendor/packaging/specifiers.py,sha256=3XBcSslm-YQEEEO_zrv4F6dR5wRxs4ErJvJ0vjNBfGM,71550
|
| 480 |
+
pip/_vendor/packaging/tags.py,sha256=ANYHZxYQVp9BlOQYOHU5ArDNVIpTFl4KlbocYVBWnFs,34236
|
| 481 |
+
pip/_vendor/packaging/utils.py,sha256=M7-JMKic2sP1YtV_8aW7eVGB-x3ADuKCiSrsVeCd2Uo,9848
|
| 482 |
+
pip/_vendor/packaging/version.py,sha256=Mcu7Tf6Y1i0gQ4FXv4t0g8cbL5joAEshjgIpz_2vISI,38393
|
| 483 |
+
pip/_vendor/pkg_resources/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
|
| 484 |
+
pip/_vendor/pkg_resources/__init__.py,sha256=vbTJ0_ruUgGxQjlEqsruFmiNPVyh2t9q-zyTDT053xI,124451
|
| 485 |
+
pip/_vendor/pkg_resources/__pycache__/__init__.cpython-314.pyc,,
|
| 486 |
+
pip/_vendor/platformdirs/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
|
| 487 |
+
pip/_vendor/platformdirs/__init__.py,sha256=gBmL_J_KdPgKiwM3dHCo-y-8CbS06QCa1XIhJ9bIgew,32389
|
| 488 |
+
pip/_vendor/platformdirs/__main__.py,sha256=DQ1o1E1l0Ghuitj4uQJKAPwkcKbGzkzO0sbhzd9RbUc,1773
|
| 489 |
+
pip/_vendor/platformdirs/__pycache__/__init__.cpython-314.pyc,,
|
| 490 |
+
pip/_vendor/platformdirs/__pycache__/__main__.cpython-314.pyc,,
|
| 491 |
+
pip/_vendor/platformdirs/__pycache__/_xdg.cpython-314.pyc,,
|
| 492 |
+
pip/_vendor/platformdirs/__pycache__/android.cpython-314.pyc,,
|
| 493 |
+
pip/_vendor/platformdirs/__pycache__/api.cpython-314.pyc,,
|
| 494 |
+
pip/_vendor/platformdirs/__pycache__/macos.cpython-314.pyc,,
|
| 495 |
+
pip/_vendor/platformdirs/__pycache__/unix.cpython-314.pyc,,
|
| 496 |
+
pip/_vendor/platformdirs/__pycache__/version.cpython-314.pyc,,
|
| 497 |
+
pip/_vendor/platformdirs/__pycache__/windows.cpython-314.pyc,,
|
| 498 |
+
pip/_vendor/platformdirs/_xdg.py,sha256=5w6S07vVabEROhaUB_X7i0hmLNbdYtC9aEvPbsEIS8M,7894
|
| 499 |
+
pip/_vendor/platformdirs/android.py,sha256=BBj3wfw-dfuVxsCsKsvFByKVspRDgfv8gwIzhpE1JRQ,11566
|
| 500 |
+
pip/_vendor/platformdirs/api.py,sha256=EaIsFgXGu8cDTb41WM3hse_SrIoOUht8_TCo5XdOOSM,14887
|
| 501 |
+
pip/_vendor/platformdirs/macos.py,sha256=PWudeDZAY9UuwLP_5rN3exSRFtYdY542hDhjah4dSWA,9538
|
| 502 |
+
pip/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 503 |
+
pip/_vendor/platformdirs/unix.py,sha256=X7Cip1xOHFOnSVveqKWzU1P7jn4zuDGpRgmVxJnk4vk,13317
|
| 504 |
+
pip/_vendor/platformdirs/version.py,sha256=dJvdm7L_7wobEcxrNoJtH7CtmYcG_VR5yNmtBshJ-xs,522
|
| 505 |
+
pip/_vendor/platformdirs/windows.py,sha256=YOdSGNhdpxm_ydZq7uG74i9mX2_WWbcMSA4fobXwL0M,15798
|
| 506 |
+
pip/_vendor/pygments/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331
|
| 507 |
+
pip/_vendor/pygments/__init__.py,sha256=Xnd_7fJUqU_6D0bxXW0qS9M_OE9G6VW8YNPbGLWoKwQ,2986
|
| 508 |
+
pip/_vendor/pygments/__main__.py,sha256=IqAQ_836Kv2CTx8r6Ie6duRQuYSPl85sbYomEwIsY14,356
|
| 509 |
+
pip/_vendor/pygments/__pycache__/__init__.cpython-314.pyc,,
|
| 510 |
+
pip/_vendor/pygments/__pycache__/__main__.cpython-314.pyc,,
|
| 511 |
+
pip/_vendor/pygments/__pycache__/console.cpython-314.pyc,,
|
| 512 |
+
pip/_vendor/pygments/__pycache__/filter.cpython-314.pyc,,
|
| 513 |
+
pip/_vendor/pygments/__pycache__/formatter.cpython-314.pyc,,
|
| 514 |
+
pip/_vendor/pygments/__pycache__/lexer.cpython-314.pyc,,
|
| 515 |
+
pip/_vendor/pygments/__pycache__/modeline.cpython-314.pyc,,
|
| 516 |
+
pip/_vendor/pygments/__pycache__/plugin.cpython-314.pyc,,
|
| 517 |
+
pip/_vendor/pygments/__pycache__/regexopt.cpython-314.pyc,,
|
| 518 |
+
pip/_vendor/pygments/__pycache__/scanner.cpython-314.pyc,,
|
| 519 |
+
pip/_vendor/pygments/__pycache__/sphinxext.cpython-314.pyc,,
|
| 520 |
+
pip/_vendor/pygments/__pycache__/style.cpython-314.pyc,,
|
| 521 |
+
pip/_vendor/pygments/__pycache__/token.cpython-314.pyc,,
|
| 522 |
+
pip/_vendor/pygments/__pycache__/unistring.cpython-314.pyc,,
|
| 523 |
+
pip/_vendor/pygments/__pycache__/util.cpython-314.pyc,,
|
| 524 |
+
pip/_vendor/pygments/console.py,sha256=C189JAwhC1Qh0AKgshzb1wVDzFqBnv1VZ45kTEDIO30,1721
|
| 525 |
+
pip/_vendor/pygments/filter.py,sha256=1dnbkq2AdC3AkHt3DaXwnOkTBLChl1kR6naSLwnr_tc,1913
|
| 526 |
+
pip/_vendor/pygments/filters/__init__.py,sha256=Prd6xUK_7kw0rQv_OXPY3871RzwtWKGp_g7SddYHBXg,40397
|
| 527 |
+
pip/_vendor/pygments/filters/__pycache__/__init__.cpython-314.pyc,,
|
| 528 |
+
pip/_vendor/pygments/formatter.py,sha256=EuAXPX6c3XQkCt3lD0VhSPSZByG7YQk8HrCwxlf-CZQ,4393
|
| 529 |
+
pip/_vendor/pygments/formatters/__init__.py,sha256=EYLDCIgBHL_DkuhNRQZlHpS2_UE5-MfsGL1hMcOa95w,5388
|
| 530 |
+
pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-314.pyc,,
|
| 531 |
+
pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-314.pyc,,
|
| 532 |
+
pip/_vendor/pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176
|
| 533 |
+
pip/_vendor/pygments/lexer.py,sha256=wSXDzt2fNZJSrlfdIfnKVlheRpFkszkPmgwBJevbBNo,35394
|
| 534 |
+
pip/_vendor/pygments/lexers/__init__.py,sha256=s8I_tVlotrM8cFTGlnjw45UY5sj5jhDwdQzOOjhzgNg,12118
|
| 535 |
+
pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-314.pyc,,
|
| 536 |
+
pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-314.pyc,,
|
| 537 |
+
pip/_vendor/pygments/lexers/__pycache__/python.cpython-314.pyc,,
|
| 538 |
+
pip/_vendor/pygments/lexers/_mapping.py,sha256=wGAYjOBprDVZA01eocwcLJ6L8biM9rP2jxwr1bQJ96M,77934
|
| 539 |
+
pip/_vendor/pygments/lexers/python.py,sha256=TqyfQOPhCYPx35apQL65CAj9k-4BxuzLq8MDfdm2jvE,54250
|
| 540 |
+
pip/_vendor/pygments/modeline.py,sha256=me8g5rySidvPtMBOrDy2O1sMqZd02lBHMY5KDZefUws,1008
|
| 541 |
+
pip/_vendor/pygments/plugin.py,sha256=P6zIw-vkSQ0k1WKHrzbBTjpwlCZPbR-6Qe_dO9Bjdu0,1928
|
| 542 |
+
pip/_vendor/pygments/regexopt.py,sha256=d2hTvazlow5zzZIOCVnfeEG2CY0GrY_igH1kCSSf7ow,3308
|
| 543 |
+
pip/_vendor/pygments/scanner.py,sha256=DtoLi1pOKpNu-6jiakJpSQLUi_ep29SQQhv7oAwYWME,3095
|
| 544 |
+
pip/_vendor/pygments/sphinxext.py,sha256=RflpnfXB4rZLYyZ-OQ_igntVOlB8jG42xFYgEVIyHf8,7984
|
| 545 |
+
pip/_vendor/pygments/style.py,sha256=wJSXOXjehGYxI6e3DGaUtONeWuhmTfswfkysrL9Was0,6423
|
| 546 |
+
pip/_vendor/pygments/styles/__init__.py,sha256=kYdCGK8gZ1V8wfFQETOzpPmZTJ1nMB0b1iNlU8dHQ9E,2045
|
| 547 |
+
pip/_vendor/pygments/styles/__pycache__/__init__.cpython-314.pyc,,
|
| 548 |
+
pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-314.pyc,,
|
| 549 |
+
pip/_vendor/pygments/styles/_mapping.py,sha256=6lovFUE29tz6EsV3XYY4hgozJ7q1JL7cfO3UOlgnS8w,3312
|
| 550 |
+
pip/_vendor/pygments/token.py,sha256=DVil5T2ltHkTgQTUYy1dMtgyigjavCs1ypE4Qf2CrGo,6229
|
| 551 |
+
pip/_vendor/pygments/unistring.py,sha256=Z4w4HfOVUhueCURRkfhAqQ2b-69UzkhY4xuHevYEy5g,63211
|
| 552 |
+
pip/_vendor/pygments/util.py,sha256=zk935tJSpwSA9zxNmV1TuhcEP0MfboXiRZSGQnLPEqk,10046
|
| 553 |
+
pip/_vendor/pyproject_hooks/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
|
| 554 |
+
pip/_vendor/pyproject_hooks/__init__.py,sha256=cPB_a9LXz5xvsRbX1o2qyAdjLatZJdQ_Lc5McNX-X7Y,691
|
| 555 |
+
pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-314.pyc,,
|
| 556 |
+
pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-314.pyc,,
|
| 557 |
+
pip/_vendor/pyproject_hooks/_impl.py,sha256=jY-raxnmyRyB57ruAitrJRUzEexuAhGTpgMygqx67Z4,14936
|
| 558 |
+
pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=MJNPpfIxcO-FghxpBbxkG1rFiQf6HOUbV4U5mq0HFns,557
|
| 559 |
+
pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-314.pyc,,
|
| 560 |
+
pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-314.pyc,,
|
| 561 |
+
pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=qcXMhmx__MIJq10gGHW3mA4Tl8dy8YzHMccwnNoKlw0,12216
|
| 562 |
+
pip/_vendor/pyproject_hooks/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 563 |
+
pip/_vendor/requests/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
| 564 |
+
pip/_vendor/requests/__init__.py,sha256=nC2attFwdoYN-oE6-vQW4ciuvIu7J2P_TU1qQ2DcueE,5873
|
| 565 |
+
pip/_vendor/requests/__pycache__/__init__.cpython-314.pyc,,
|
| 566 |
+
pip/_vendor/requests/__pycache__/__version__.cpython-314.pyc,,
|
| 567 |
+
pip/_vendor/requests/__pycache__/_internal_utils.cpython-314.pyc,,
|
| 568 |
+
pip/_vendor/requests/__pycache__/_types.cpython-314.pyc,,
|
| 569 |
+
pip/_vendor/requests/__pycache__/adapters.cpython-314.pyc,,
|
| 570 |
+
pip/_vendor/requests/__pycache__/api.cpython-314.pyc,,
|
| 571 |
+
pip/_vendor/requests/__pycache__/auth.cpython-314.pyc,,
|
| 572 |
+
pip/_vendor/requests/__pycache__/certs.cpython-314.pyc,,
|
| 573 |
+
pip/_vendor/requests/__pycache__/compat.cpython-314.pyc,,
|
| 574 |
+
pip/_vendor/requests/__pycache__/cookies.cpython-314.pyc,,
|
| 575 |
+
pip/_vendor/requests/__pycache__/exceptions.cpython-314.pyc,,
|
| 576 |
+
pip/_vendor/requests/__pycache__/help.cpython-314.pyc,,
|
| 577 |
+
pip/_vendor/requests/__pycache__/hooks.cpython-314.pyc,,
|
| 578 |
+
pip/_vendor/requests/__pycache__/models.cpython-314.pyc,,
|
| 579 |
+
pip/_vendor/requests/__pycache__/packages.cpython-314.pyc,,
|
| 580 |
+
pip/_vendor/requests/__pycache__/sessions.cpython-314.pyc,,
|
| 581 |
+
pip/_vendor/requests/__pycache__/status_codes.cpython-314.pyc,,
|
| 582 |
+
pip/_vendor/requests/__pycache__/structures.cpython-314.pyc,,
|
| 583 |
+
pip/_vendor/requests/__pycache__/utils.cpython-314.pyc,,
|
| 584 |
+
pip/_vendor/requests/__version__.py,sha256=q7qQZwsDcKPcQNl8LepGo11L7JQf8Q18PPMJjvBKF_k,435
|
| 585 |
+
pip/_vendor/requests/_internal_utils.py,sha256=TH2NEyyYmPx9cV5HPzrHR4XdxKuW0skkD4eDXcbZgf8,1542
|
| 586 |
+
pip/_vendor/requests/_types.py,sha256=0G33n1SiecDMP67FlsRGJG1a0U7f1SnrMo3vvWKkSok,5838
|
| 587 |
+
pip/_vendor/requests/adapters.py,sha256=5kSKhXlJuR_enrOjd3khSGpRBj1WqCh08Qssu0CEZ8M,28208
|
| 588 |
+
pip/_vendor/requests/api.py,sha256=TRVICsBG8Ikgl5joZQR270oo6-b4G0AHWPjvQuxrVQk,7152
|
| 589 |
+
pip/_vendor/requests/auth.py,sha256=6TlRpVLUw8X9m4Sl4dSbA8qrpHI8T_ES2mBTYA7IWmU,12233
|
| 590 |
+
pip/_vendor/requests/certs.py,sha256=eD1G0RoMZ3kA0lydkw7oo0lmcKqvD1hhz6yOlgSKm8w,442
|
| 591 |
+
pip/_vendor/requests/compat.py,sha256=1UheFGomceWe7aysgboYWxcEOH_HpQFmKhIyPiAanog,2035
|
| 592 |
+
pip/_vendor/requests/cookies.py,sha256=ChzmFA8rlCBSLFCA41bmifp5bCUi9VyhCCb17Y4Kcjw,21549
|
| 593 |
+
pip/_vendor/requests/exceptions.py,sha256=Am6y4ncoBptpmesOjRxc5iiuBDYjx3UN1oRVBlNgRyE,4576
|
| 594 |
+
pip/_vendor/requests/help.py,sha256=TVWejkCROwKexHHB44KCZSr-c0o5XakNL7LT1UJlRy8,4114
|
| 595 |
+
pip/_vendor/requests/hooks.py,sha256=69igJHXTGg5HOo9VPpUB_0NkW5VjiFrVKETnpj8Ndqs,1138
|
| 596 |
+
pip/_vendor/requests/models.py,sha256=99d7WF4pR35NHo9jrbGiBocZ3LCMsloVU0DAcQ49x3Q,41848
|
| 597 |
+
pip/_vendor/requests/packages.py,sha256=_ZQDCJTJ8SP3kVWunSqBsRZNPzj2c1WFVqbdr08pz3U,1057
|
| 598 |
+
pip/_vendor/requests/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 599 |
+
pip/_vendor/requests/sessions.py,sha256=lvuzC7vwalmlJo0TtXiFFJdWqj8xaVtcFeQd17svZ6Y,34248
|
| 600 |
+
pip/_vendor/requests/status_codes.py,sha256=GVD0fInPGAGXh-B9jOSPZtazjmIvfZTXCpAkf-5sBA4,4351
|
| 601 |
+
pip/_vendor/requests/structures.py,sha256=upRgw5B48l5vHSokrJQaxvjS7pcZf6jI0MJi2KHmegI,4134
|
| 602 |
+
pip/_vendor/requests/utils.py,sha256=u7MICyzQTvfUa3asg9nBvcrnhSIPEc6qhJSxiGGwTOk,36334
|
| 603 |
+
pip/_vendor/resolvelib/LICENSE,sha256=84j9OMrRMRLB3A9mm76A5_hFQe26-3LzAw0sp2QsPJ0,751
|
| 604 |
+
pip/_vendor/resolvelib/__init__.py,sha256=yoX-d4STvwGGCiQRE5cJC9Cter69SgVgqClxOCvSP7M,541
|
| 605 |
+
pip/_vendor/resolvelib/__pycache__/__init__.cpython-314.pyc,,
|
| 606 |
+
pip/_vendor/resolvelib/__pycache__/providers.cpython-314.pyc,,
|
| 607 |
+
pip/_vendor/resolvelib/__pycache__/reporters.cpython-314.pyc,,
|
| 608 |
+
pip/_vendor/resolvelib/__pycache__/structs.cpython-314.pyc,,
|
| 609 |
+
pip/_vendor/resolvelib/providers.py,sha256=pIWJbIdJJ9GFtNbtwTH0Ia43Vj6hYCEJj2DOLue15FM,8914
|
| 610 |
+
pip/_vendor/resolvelib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 611 |
+
pip/_vendor/resolvelib/reporters.py,sha256=pNJf4nFxLpAeKxlBUi2GEj0a2Ij1nikY0UabTKXesT4,2037
|
| 612 |
+
pip/_vendor/resolvelib/resolvers/__init__.py,sha256=728M3EvmnPbVXS7ExXlv2kMu6b7wEsoPutEfl-uVk_I,640
|
| 613 |
+
pip/_vendor/resolvelib/resolvers/__pycache__/__init__.cpython-314.pyc,,
|
| 614 |
+
pip/_vendor/resolvelib/resolvers/__pycache__/abstract.cpython-314.pyc,,
|
| 615 |
+
pip/_vendor/resolvelib/resolvers/__pycache__/criterion.cpython-314.pyc,,
|
| 616 |
+
pip/_vendor/resolvelib/resolvers/__pycache__/exceptions.cpython-314.pyc,,
|
| 617 |
+
pip/_vendor/resolvelib/resolvers/__pycache__/resolution.cpython-314.pyc,,
|
| 618 |
+
pip/_vendor/resolvelib/resolvers/abstract.py,sha256=CNeQPnpAudY77nmzOkONSmAgRlzIf06X-X9mvRYODms,1543
|
| 619 |
+
pip/_vendor/resolvelib/resolvers/criterion.py,sha256=lcmZGv5sKHOnFD_RzZwvlGSj19MeA-5rCMpdf2Sgw7Y,1768
|
| 620 |
+
pip/_vendor/resolvelib/resolvers/exceptions.py,sha256=ln_jaQtgLlRUSFY627yiHG2gD7AgaXzRKaElFVh7fDQ,1768
|
| 621 |
+
pip/_vendor/resolvelib/resolvers/resolution.py,sha256=3J_zkW-sD3EY-BlNXjyln__njpyH5n0UZJT6uV7CheA,24212
|
| 622 |
+
pip/_vendor/resolvelib/structs.py,sha256=pu-EJiR2IBITr2SQeNPRa0rXhjlStfmO_GEgAhr3004,6420
|
| 623 |
+
pip/_vendor/rich/LICENSE,sha256=3u18F6QxgVgZCj6iOcyHmlpQJxzruYrnAl9I--WNyhU,1056
|
| 624 |
+
pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090
|
| 625 |
+
pip/_vendor/rich/__main__.py,sha256=e_aVC-tDzarWQW9SuZMuCgBr6ODV_iDNV2Wh2xkxOlw,7896
|
| 626 |
+
pip/_vendor/rich/__pycache__/__init__.cpython-314.pyc,,
|
| 627 |
+
pip/_vendor/rich/__pycache__/__main__.cpython-314.pyc,,
|
| 628 |
+
pip/_vendor/rich/__pycache__/_cell_widths.cpython-314.pyc,,
|
| 629 |
+
pip/_vendor/rich/__pycache__/_emoji_codes.cpython-314.pyc,,
|
| 630 |
+
pip/_vendor/rich/__pycache__/_emoji_replace.cpython-314.pyc,,
|
| 631 |
+
pip/_vendor/rich/__pycache__/_export_format.cpython-314.pyc,,
|
| 632 |
+
pip/_vendor/rich/__pycache__/_extension.cpython-314.pyc,,
|
| 633 |
+
pip/_vendor/rich/__pycache__/_fileno.cpython-314.pyc,,
|
| 634 |
+
pip/_vendor/rich/__pycache__/_inspect.cpython-314.pyc,,
|
| 635 |
+
pip/_vendor/rich/__pycache__/_log_render.cpython-314.pyc,,
|
| 636 |
+
pip/_vendor/rich/__pycache__/_loop.cpython-314.pyc,,
|
| 637 |
+
pip/_vendor/rich/__pycache__/_null_file.cpython-314.pyc,,
|
| 638 |
+
pip/_vendor/rich/__pycache__/_palettes.cpython-314.pyc,,
|
| 639 |
+
pip/_vendor/rich/__pycache__/_pick.cpython-314.pyc,,
|
| 640 |
+
pip/_vendor/rich/__pycache__/_ratio.cpython-314.pyc,,
|
| 641 |
+
pip/_vendor/rich/__pycache__/_spinners.cpython-314.pyc,,
|
| 642 |
+
pip/_vendor/rich/__pycache__/_stack.cpython-314.pyc,,
|
| 643 |
+
pip/_vendor/rich/__pycache__/_timer.cpython-314.pyc,,
|
| 644 |
+
pip/_vendor/rich/__pycache__/_win32_console.cpython-314.pyc,,
|
| 645 |
+
pip/_vendor/rich/__pycache__/_windows.cpython-314.pyc,,
|
| 646 |
+
pip/_vendor/rich/__pycache__/_windows_renderer.cpython-314.pyc,,
|
| 647 |
+
pip/_vendor/rich/__pycache__/_wrap.cpython-314.pyc,,
|
| 648 |
+
pip/_vendor/rich/__pycache__/abc.cpython-314.pyc,,
|
| 649 |
+
pip/_vendor/rich/__pycache__/align.cpython-314.pyc,,
|
| 650 |
+
pip/_vendor/rich/__pycache__/ansi.cpython-314.pyc,,
|
| 651 |
+
pip/_vendor/rich/__pycache__/bar.cpython-314.pyc,,
|
| 652 |
+
pip/_vendor/rich/__pycache__/box.cpython-314.pyc,,
|
| 653 |
+
pip/_vendor/rich/__pycache__/cells.cpython-314.pyc,,
|
| 654 |
+
pip/_vendor/rich/__pycache__/color.cpython-314.pyc,,
|
| 655 |
+
pip/_vendor/rich/__pycache__/color_triplet.cpython-314.pyc,,
|
| 656 |
+
pip/_vendor/rich/__pycache__/columns.cpython-314.pyc,,
|
| 657 |
+
pip/_vendor/rich/__pycache__/console.cpython-314.pyc,,
|
| 658 |
+
pip/_vendor/rich/__pycache__/constrain.cpython-314.pyc,,
|
| 659 |
+
pip/_vendor/rich/__pycache__/containers.cpython-314.pyc,,
|
| 660 |
+
pip/_vendor/rich/__pycache__/control.cpython-314.pyc,,
|
| 661 |
+
pip/_vendor/rich/__pycache__/default_styles.cpython-314.pyc,,
|
| 662 |
+
pip/_vendor/rich/__pycache__/diagnose.cpython-314.pyc,,
|
| 663 |
+
pip/_vendor/rich/__pycache__/emoji.cpython-314.pyc,,
|
| 664 |
+
pip/_vendor/rich/__pycache__/errors.cpython-314.pyc,,
|
| 665 |
+
pip/_vendor/rich/__pycache__/file_proxy.cpython-314.pyc,,
|
| 666 |
+
pip/_vendor/rich/__pycache__/filesize.cpython-314.pyc,,
|
| 667 |
+
pip/_vendor/rich/__pycache__/highlighter.cpython-314.pyc,,
|
| 668 |
+
pip/_vendor/rich/__pycache__/json.cpython-314.pyc,,
|
| 669 |
+
pip/_vendor/rich/__pycache__/jupyter.cpython-314.pyc,,
|
| 670 |
+
pip/_vendor/rich/__pycache__/layout.cpython-314.pyc,,
|
| 671 |
+
pip/_vendor/rich/__pycache__/live.cpython-314.pyc,,
|
| 672 |
+
pip/_vendor/rich/__pycache__/live_render.cpython-314.pyc,,
|
| 673 |
+
pip/_vendor/rich/__pycache__/logging.cpython-314.pyc,,
|
| 674 |
+
pip/_vendor/rich/__pycache__/markup.cpython-314.pyc,,
|
| 675 |
+
pip/_vendor/rich/__pycache__/measure.cpython-314.pyc,,
|
| 676 |
+
pip/_vendor/rich/__pycache__/padding.cpython-314.pyc,,
|
| 677 |
+
pip/_vendor/rich/__pycache__/pager.cpython-314.pyc,,
|
| 678 |
+
pip/_vendor/rich/__pycache__/palette.cpython-314.pyc,,
|
| 679 |
+
pip/_vendor/rich/__pycache__/panel.cpython-314.pyc,,
|
| 680 |
+
pip/_vendor/rich/__pycache__/pretty.cpython-314.pyc,,
|
| 681 |
+
pip/_vendor/rich/__pycache__/progress.cpython-314.pyc,,
|
| 682 |
+
pip/_vendor/rich/__pycache__/progress_bar.cpython-314.pyc,,
|
| 683 |
+
pip/_vendor/rich/__pycache__/prompt.cpython-314.pyc,,
|
| 684 |
+
pip/_vendor/rich/__pycache__/protocol.cpython-314.pyc,,
|
| 685 |
+
pip/_vendor/rich/__pycache__/region.cpython-314.pyc,,
|
| 686 |
+
pip/_vendor/rich/__pycache__/repr.cpython-314.pyc,,
|
| 687 |
+
pip/_vendor/rich/__pycache__/rule.cpython-314.pyc,,
|
| 688 |
+
pip/_vendor/rich/__pycache__/scope.cpython-314.pyc,,
|
| 689 |
+
pip/_vendor/rich/__pycache__/screen.cpython-314.pyc,,
|
| 690 |
+
pip/_vendor/rich/__pycache__/segment.cpython-314.pyc,,
|
| 691 |
+
pip/_vendor/rich/__pycache__/spinner.cpython-314.pyc,,
|
| 692 |
+
pip/_vendor/rich/__pycache__/status.cpython-314.pyc,,
|
| 693 |
+
pip/_vendor/rich/__pycache__/style.cpython-314.pyc,,
|
| 694 |
+
pip/_vendor/rich/__pycache__/styled.cpython-314.pyc,,
|
| 695 |
+
pip/_vendor/rich/__pycache__/syntax.cpython-314.pyc,,
|
| 696 |
+
pip/_vendor/rich/__pycache__/table.cpython-314.pyc,,
|
| 697 |
+
pip/_vendor/rich/__pycache__/terminal_theme.cpython-314.pyc,,
|
| 698 |
+
pip/_vendor/rich/__pycache__/text.cpython-314.pyc,,
|
| 699 |
+
pip/_vendor/rich/__pycache__/theme.cpython-314.pyc,,
|
| 700 |
+
pip/_vendor/rich/__pycache__/themes.cpython-314.pyc,,
|
| 701 |
+
pip/_vendor/rich/__pycache__/traceback.cpython-314.pyc,,
|
| 702 |
+
pip/_vendor/rich/__pycache__/tree.cpython-314.pyc,,
|
| 703 |
+
pip/_vendor/rich/_cell_widths.py,sha256=fbmeyetEdHjzE_Vx2l1uK7tnPOhMs2X1lJfO3vsKDpA,10209
|
| 704 |
+
pip/_vendor/rich/_emoji_codes.py,sha256=hu1VL9nbVdppJrVoijVshRlcRRe_v3dju3Mmd2sKZdY,140235
|
| 705 |
+
pip/_vendor/rich/_emoji_replace.py,sha256=n-kcetsEUx2ZUmhQrfeMNc-teeGhpuSQ5F8VPBsyvDo,1064
|
| 706 |
+
pip/_vendor/rich/_export_format.py,sha256=RI08pSrm5tBSzPMvnbTqbD9WIalaOoN5d4M1RTmLq1Y,2128
|
| 707 |
+
pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-34,265
|
| 708 |
+
pip/_vendor/rich/_fileno.py,sha256=HWZxP5C2ajMbHryvAQZseflVfQoGzsKOHzKGsLD8ynQ,799
|
| 709 |
+
pip/_vendor/rich/_inspect.py,sha256=ROT0PLC2GMWialWZkqJIjmYq7INRijQQkoSokWTaAiI,9656
|
| 710 |
+
pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225
|
| 711 |
+
pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236
|
| 712 |
+
pip/_vendor/rich/_null_file.py,sha256=ADGKp1yt-k70FMKV6tnqCqecB-rSJzp-WQsD7LPL-kg,1394
|
| 713 |
+
pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063
|
| 714 |
+
pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423
|
| 715 |
+
pip/_vendor/rich/_ratio.py,sha256=IOtl78sQCYZsmHyxhe45krkb68u9xVz7zFsXVJD-b2Y,5325
|
| 716 |
+
pip/_vendor/rich/_spinners.py,sha256=U2r1_g_1zSjsjiUdAESc2iAMc3i4ri_S8PYP6kQ5z1I,19919
|
| 717 |
+
pip/_vendor/rich/_stack.py,sha256=-C8OK7rxn3sIUdVwxZBBpeHhIzX0eI-VM3MemYfaXm0,351
|
| 718 |
+
pip/_vendor/rich/_timer.py,sha256=zelxbT6oPFZnNrwWPpc1ktUeAT-Vc4fuFcRZLQGLtMI,417
|
| 719 |
+
pip/_vendor/rich/_win32_console.py,sha256=BSaDRIMwBLITn_m0mTRLPqME5q-quGdSMuYMpYeYJwc,22755
|
| 720 |
+
pip/_vendor/rich/_windows.py,sha256=aBwaD_S56SbgopIvayVmpk0Y28uwY2C5Bab1wl3Bp-I,1925
|
| 721 |
+
pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQleHCJerlk,2783
|
| 722 |
+
pip/_vendor/rich/_wrap.py,sha256=FlSsom5EX0LVkA3KWy34yHnCfLtqX-ZIepXKh-70rpc,3404
|
| 723 |
+
pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890
|
| 724 |
+
pip/_vendor/rich/align.py,sha256=dg-7uY0ukMLLlUEsBDRLva22_sQgIJD4BK0dmZHFHug,10324
|
| 725 |
+
pip/_vendor/rich/ansi.py,sha256=Avs1LHbSdcyOvDOdpELZUoULcBiYewY76eNBp6uFBhs,6921
|
| 726 |
+
pip/_vendor/rich/bar.py,sha256=ldbVHOzKJOnflVNuv1xS7g6dLX2E3wMnXkdPbpzJTcs,3263
|
| 727 |
+
pip/_vendor/rich/box.py,sha256=kmavBc_dn73L_g_8vxWSwYJD2uzBXOUFTtJOfpbczcM,10686
|
| 728 |
+
pip/_vendor/rich/cells.py,sha256=KrQkj5-LghCCpJLSNQIyAZjndc4bnEqOEmi5YuZ9UCY,5130
|
| 729 |
+
pip/_vendor/rich/color.py,sha256=3HSULVDj7qQkXUdFWv78JOiSZzfy5y1nkcYhna296V0,18211
|
| 730 |
+
pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054
|
| 731 |
+
pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131
|
| 732 |
+
pip/_vendor/rich/console.py,sha256=t9azZpmRMVU5cphVBZSShNsmBxd2-IAWcTTlhor-E1s,100849
|
| 733 |
+
pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288
|
| 734 |
+
pip/_vendor/rich/containers.py,sha256=c_56TxcedGYqDepHBMTuZdUIijitAQgnox-Qde0Z1qo,5502
|
| 735 |
+
pip/_vendor/rich/control.py,sha256=EUTSUFLQbxY6Zmo_sdM-5Ls323vIHTBfN8TPulqeHUY,6487
|
| 736 |
+
pip/_vendor/rich/default_styles.py,sha256=khQFqqaoDs3bprMqWpHw8nO5UpG2DN6QtuTd6LzZwYc,8257
|
| 737 |
+
pip/_vendor/rich/diagnose.py,sha256=fJl1TItRn19gGwouqTg-8zPUW3YqQBqGltrfPQs1H9w,1025
|
| 738 |
+
pip/_vendor/rich/emoji.py,sha256=Wd4bQubZdSy6-PyrRQNuMHtn2VkljK9uPZPVlu2cmx0,2367
|
| 739 |
+
pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642
|
| 740 |
+
pip/_vendor/rich/file_proxy.py,sha256=Tl9THMDZ-Pk5Wm8sI1gGg_U5DhusmxD-FZ0fUbcU0W0,1683
|
| 741 |
+
pip/_vendor/rich/filesize.py,sha256=_iz9lIpRgvW7MNSeCZnLg-HwzbP4GETg543WqD8SFs0,2484
|
| 742 |
+
pip/_vendor/rich/highlighter.py,sha256=G_sn-8DKjM1sEjLG_oc4ovkWmiUpWvj8bXi0yed2LnY,9586
|
| 743 |
+
pip/_vendor/rich/json.py,sha256=vVEoKdawoJRjAFayPwXkMBPLy7RSTs-f44wSQDR2nJ0,5031
|
| 744 |
+
pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252
|
| 745 |
+
pip/_vendor/rich/layout.py,sha256=ajkSFAtEVv9EFTcFs-w4uZfft7nEXhNzL7ZVdgrT5rI,14004
|
| 746 |
+
pip/_vendor/rich/live.py,sha256=tF3ukAAJZ_N2ZbGclqZ-iwLoIoZ8f0HHUz79jAyJqj8,15180
|
| 747 |
+
pip/_vendor/rich/live_render.py,sha256=It_39YdzrBm8o3LL0kaGorPFg-BfZWAcrBjLjFokbx4,3521
|
| 748 |
+
pip/_vendor/rich/logging.py,sha256=5KaPPSMP9FxcXPBcKM4cGd_zW78PMgf-YbMVnvfSw0o,12468
|
| 749 |
+
pip/_vendor/rich/markup.py,sha256=3euGKP5s41NCQwaSjTnJxus5iZMHjxpIM0W6fCxra38,8451
|
| 750 |
+
pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305
|
| 751 |
+
pip/_vendor/rich/padding.py,sha256=KVEI3tOwo9sgK1YNSuH__M1_jUWmLZwRVV_KmOtVzyM,4908
|
| 752 |
+
pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828
|
| 753 |
+
pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396
|
| 754 |
+
pip/_vendor/rich/panel.py,sha256=9sQl00hPIqH5G2gALQo4NepFwpP0k9wT-s_gOms5pIc,11157
|
| 755 |
+
pip/_vendor/rich/pretty.py,sha256=gy3S72u4FRg2ytoo7N1ZDWDIvB4unbzd5iUGdgm-8fc,36391
|
| 756 |
+
pip/_vendor/rich/progress.py,sha256=CUc2lkU-X59mVdGfjMCBkZeiGPL3uxdONjhNJF2T7wY,60408
|
| 757 |
+
pip/_vendor/rich/progress_bar.py,sha256=mZTPpJUwcfcdgQCTTz3kyY-fc79ddLwtx6Ghhxfo064,8162
|
| 758 |
+
pip/_vendor/rich/prompt.py,sha256=l0RhQU-0UVTV9e08xW1BbIj0Jq2IXyChX4lC0lFNzt4,12447
|
| 759 |
+
pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391
|
| 760 |
+
pip/_vendor/rich/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 761 |
+
pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166
|
| 762 |
+
pip/_vendor/rich/repr.py,sha256=5MZJZmONgC6kud-QW-_m1okXwL2aR6u6y-pUcUCJz28,4431
|
| 763 |
+
pip/_vendor/rich/rule.py,sha256=0fNaS_aERa3UMRc3T5WMpN_sumtDxfaor2y3of1ftBk,4602
|
| 764 |
+
pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843
|
| 765 |
+
pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591
|
| 766 |
+
pip/_vendor/rich/segment.py,sha256=otnKeKGEV-WRlQVosfJVeFDcDxAKHpvJ_hLzSu5lumM,24743
|
| 767 |
+
pip/_vendor/rich/spinner.py,sha256=onIhpKlljRHppTZasxO8kXgtYyCHUkpSgKglRJ3o51g,4214
|
| 768 |
+
pip/_vendor/rich/status.py,sha256=kkPph3YeAZBo-X-4wPp8gTqZyU466NLwZBA4PZTTewo,4424
|
| 769 |
+
pip/_vendor/rich/style.py,sha256=W9Ccy8Py8lNICtlfcp-ryzMTuQaGxAU3av7-g5fHu0s,26990
|
| 770 |
+
pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258
|
| 771 |
+
pip/_vendor/rich/syntax.py,sha256=eDKIRwl--eZ0Lwo2da2RRtfutXGavrJO61Cl5OkS59U,36371
|
| 772 |
+
pip/_vendor/rich/table.py,sha256=ZmT7V7MMCOYKw7TGY9SZLyYDf6JdM-WVf07FdVuVhTI,40049
|
| 773 |
+
pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370
|
| 774 |
+
pip/_vendor/rich/text.py,sha256=AO7JPCz6-gaN1thVLXMBntEmDPVYFgFNG1oM61_sanU,47552
|
| 775 |
+
pip/_vendor/rich/theme.py,sha256=oNyhXhGagtDlbDye3tVu3esWOWk0vNkuxFw-_unlaK0,3771
|
| 776 |
+
pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102
|
| 777 |
+
pip/_vendor/rich/traceback.py,sha256=c0WmB_L04_UfZbLaoH982_U_s7eosxKMUiAVmDPdRYU,35861
|
| 778 |
+
pip/_vendor/rich/tree.py,sha256=yWnQ6rAvRGJ3qZGqBrxS2SW2TKBTNrP0SdY8QxOFPuw,9451
|
| 779 |
+
pip/_vendor/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
|
| 780 |
+
pip/_vendor/tomli/__init__.py,sha256=nrBC18DbXRTCFo7ElG5BDeWpHJzOhokvXk215GM8Z2I,314
|
| 781 |
+
pip/_vendor/tomli/__pycache__/__init__.cpython-314.pyc,,
|
| 782 |
+
pip/_vendor/tomli/__pycache__/_parser.cpython-314.pyc,,
|
| 783 |
+
pip/_vendor/tomli/__pycache__/_re.cpython-314.pyc,,
|
| 784 |
+
pip/_vendor/tomli/__pycache__/_types.cpython-314.pyc,,
|
| 785 |
+
pip/_vendor/tomli/_parser.py,sha256=pBIjTIa_cQs2HglDJ2lh8OJfptfDa6eg5-7Iej4BjHs,26440
|
| 786 |
+
pip/_vendor/tomli/_re.py,sha256=oSNZ_ilFI6chEuQ01YRSoUydBQr_okF_mSdHTkFmv90,3396
|
| 787 |
+
pip/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
|
| 788 |
+
pip/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
| 789 |
+
pip/_vendor/tomli_w/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
|
| 790 |
+
pip/_vendor/tomli_w/__init__.py,sha256=0F8yDtXx3Uunhm874KrAcP76srsM98y7WyHQwCulZbo,169
|
| 791 |
+
pip/_vendor/tomli_w/__pycache__/__init__.cpython-314.pyc,,
|
| 792 |
+
pip/_vendor/tomli_w/__pycache__/_writer.cpython-314.pyc,,
|
| 793 |
+
pip/_vendor/tomli_w/_writer.py,sha256=dsifFS2xYf1i76mmRyfz9y125xC7Z_HQ845ZKhJsYXs,6961
|
| 794 |
+
pip/_vendor/tomli_w/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
| 795 |
+
pip/_vendor/truststore/LICENSE,sha256=M757fo-k_Rmxdg4ajtimaL2rhSyRtpLdQUJLy3Jan8o,1086
|
| 796 |
+
pip/_vendor/truststore/__init__.py,sha256=Bu7kqkmpunhLsj5xCu8gT_25ktoPXcSnwe8VHk1GmJo,1320
|
| 797 |
+
pip/_vendor/truststore/__pycache__/__init__.cpython-314.pyc,,
|
| 798 |
+
pip/_vendor/truststore/__pycache__/_api.cpython-314.pyc,,
|
| 799 |
+
pip/_vendor/truststore/__pycache__/_macos.cpython-314.pyc,,
|
| 800 |
+
pip/_vendor/truststore/__pycache__/_openssl.cpython-314.pyc,,
|
| 801 |
+
pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-314.pyc,,
|
| 802 |
+
pip/_vendor/truststore/__pycache__/_windows.cpython-314.pyc,,
|
| 803 |
+
pip/_vendor/truststore/_api.py,sha256=CYJCV5BTfttZYfqY3movdMBE-8az7uhET_LYbKT2Nn4,11413
|
| 804 |
+
pip/_vendor/truststore/_macos.py,sha256=nZlLkOmszUE0g6ryRwBVGY5COzPyudcsiJtDWarM5LQ,20503
|
| 805 |
+
pip/_vendor/truststore/_openssl.py,sha256=zB-SQvJydks7tQ0yIwrP6GD3fQNSSaPiq7zw4yF5T40,2412
|
| 806 |
+
pip/_vendor/truststore/_ssl_constants.py,sha256=NUD4fVKdSD02ri7-db0tnO0VqLP9aHuzmStcW7tAl08,1130
|
| 807 |
+
pip/_vendor/truststore/_windows.py,sha256=rAHyKYD8M7t-bXfG8VgOVa3TpfhVhbt4rZQlO45YuP8,17993
|
| 808 |
+
pip/_vendor/truststore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 809 |
+
pip/_vendor/urllib3/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093
|
| 810 |
+
pip/_vendor/urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979
|
| 811 |
+
pip/_vendor/urllib3/__pycache__/__init__.cpython-314.pyc,,
|
| 812 |
+
pip/_vendor/urllib3/__pycache__/_base_connection.cpython-314.pyc,,
|
| 813 |
+
pip/_vendor/urllib3/__pycache__/_collections.cpython-314.pyc,,
|
| 814 |
+
pip/_vendor/urllib3/__pycache__/_request_methods.cpython-314.pyc,,
|
| 815 |
+
pip/_vendor/urllib3/__pycache__/_version.cpython-314.pyc,,
|
| 816 |
+
pip/_vendor/urllib3/__pycache__/connection.cpython-314.pyc,,
|
| 817 |
+
pip/_vendor/urllib3/__pycache__/connectionpool.cpython-314.pyc,,
|
| 818 |
+
pip/_vendor/urllib3/__pycache__/exceptions.cpython-314.pyc,,
|
| 819 |
+
pip/_vendor/urllib3/__pycache__/fields.cpython-314.pyc,,
|
| 820 |
+
pip/_vendor/urllib3/__pycache__/filepost.cpython-314.pyc,,
|
| 821 |
+
pip/_vendor/urllib3/__pycache__/poolmanager.cpython-314.pyc,,
|
| 822 |
+
pip/_vendor/urllib3/__pycache__/response.cpython-314.pyc,,
|
| 823 |
+
pip/_vendor/urllib3/_base_connection.py,sha256=HzcSEHexgDrRUr60jNniB2KQjdw97SedD5luRfHtCXg,5580
|
| 824 |
+
pip/_vendor/urllib3/_collections.py,sha256=aOVm2mKilvuvT1efAGtkA7pi65C1NC3HJfpFxvYBS8A,17522
|
| 825 |
+
pip/_vendor/urllib3/_request_methods.py,sha256=gCeF85SO_UU4WoPwYHIoz_tw-eM_EVOkLFp8OFsC7DA,9931
|
| 826 |
+
pip/_vendor/urllib3/_version.py,sha256=-gMrKhsPiOj0XzUezVFa59d1S6QgQiKgQT5gGgyM8-w,520
|
| 827 |
+
pip/_vendor/urllib3/connection.py,sha256=Zos3qxKDW9-GQ6aVqfBQVRM5soB0IjHxY_xXWpJaFTI,42786
|
| 828 |
+
pip/_vendor/urllib3/connectionpool.py,sha256=sGFnddXYwlx7KC4JCP1gKvdNGLNK-YTCJDdGACGj3Y8,44164
|
| 829 |
+
pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 830 |
+
pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-314.pyc,,
|
| 831 |
+
pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-314.pyc,,
|
| 832 |
+
pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-314.pyc,,
|
| 833 |
+
pip/_vendor/urllib3/contrib/emscripten/__init__.py,sha256=ZruXaWKVzAEJdqNH3NEh0mrHrw--2VZYb0zX0RonNZA,870
|
| 834 |
+
pip/_vendor/urllib3/contrib/emscripten/__pycache__/__init__.cpython-314.pyc,,
|
| 835 |
+
pip/_vendor/urllib3/contrib/emscripten/__pycache__/connection.cpython-314.pyc,,
|
| 836 |
+
pip/_vendor/urllib3/contrib/emscripten/__pycache__/fetch.cpython-314.pyc,,
|
| 837 |
+
pip/_vendor/urllib3/contrib/emscripten/__pycache__/request.cpython-314.pyc,,
|
| 838 |
+
pip/_vendor/urllib3/contrib/emscripten/__pycache__/response.cpython-314.pyc,,
|
| 839 |
+
pip/_vendor/urllib3/contrib/emscripten/connection.py,sha256=giElsBoUsKVURbZzb8GCrJmqW23Xnvj2aNyQVF42slg,8960
|
| 840 |
+
pip/_vendor/urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=z1k3zZ4_hDKd3-tN7wzz8LHjHC2pxN_uu8B3k9D9A3c,3677
|
| 841 |
+
pip/_vendor/urllib3/contrib/emscripten/fetch.py,sha256=5xcd--viFxZd2nBy0aK73dtJ9Tsh1yYZU_SUXwnwibk,23520
|
| 842 |
+
pip/_vendor/urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566
|
| 843 |
+
pip/_vendor/urllib3/contrib/emscripten/response.py,sha256=CDpY0GFoluR3IxECkM2gH5uDfYDM0EL1FWr7B76wtgM,9719
|
| 844 |
+
pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=Pi_qwvdl83tNpuj1k5pCpP_gmxlk4BfFrTlAeg-qZ_U,19760
|
| 845 |
+
pip/_vendor/urllib3/contrib/socks.py,sha256=rSuklfha4-vto-8qSLhSPmf5lmRPIYuqdAxKe9bg2RA,7639
|
| 846 |
+
pip/_vendor/urllib3/exceptions.py,sha256=hQPHoqo4yw46esNSVkciVQXVUgAxWAglsh6MbyQta-Q,9945
|
| 847 |
+
pip/_vendor/urllib3/fields.py,sha256=aGLFAVZpVU-FbJlllve4Ahg0-pH9ZZBQ2Iz7lfpkjkM,10801
|
| 848 |
+
pip/_vendor/urllib3/filepost.py,sha256=U8eNZ-mpKKHhrlbHEEiTxxgK16IejhEa7uz42yqA_dI,2388
|
| 849 |
+
pip/_vendor/urllib3/http2/__init__.py,sha256=xzrASH7R5ANRkPJOot5lGnATOq3KKuyXzI42rcnwmqs,1741
|
| 850 |
+
pip/_vendor/urllib3/http2/__pycache__/__init__.cpython-314.pyc,,
|
| 851 |
+
pip/_vendor/urllib3/http2/__pycache__/connection.cpython-314.pyc,,
|
| 852 |
+
pip/_vendor/urllib3/http2/__pycache__/probe.cpython-314.pyc,,
|
| 853 |
+
pip/_vendor/urllib3/http2/connection.py,sha256=bHMH6fNvatwXPrKqrcn74yA3pUWcqPDppnK1LcKCbP8,12578
|
| 854 |
+
pip/_vendor/urllib3/http2/probe.py,sha256=nnAkqbhAakOiF75rz7W0udZ38Eeh_uD8fjV74N73FEI,3014
|
| 855 |
+
pip/_vendor/urllib3/poolmanager.py,sha256=grIrETG1rgHYBaQau1yN4xxfK8U9CKVRYmwBXW_kdSo,23929
|
| 856 |
+
pip/_vendor/urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93
|
| 857 |
+
pip/_vendor/urllib3/response.py,sha256=DzBM-dzl6x_h2EZGc9iw0gbOkQ36bolC7WESH4TurH8,53031
|
| 858 |
+
pip/_vendor/urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001
|
| 859 |
+
pip/_vendor/urllib3/util/__pycache__/__init__.cpython-314.pyc,,
|
| 860 |
+
pip/_vendor/urllib3/util/__pycache__/connection.cpython-314.pyc,,
|
| 861 |
+
pip/_vendor/urllib3/util/__pycache__/proxy.cpython-314.pyc,,
|
| 862 |
+
pip/_vendor/urllib3/util/__pycache__/request.cpython-314.pyc,,
|
| 863 |
+
pip/_vendor/urllib3/util/__pycache__/response.cpython-314.pyc,,
|
| 864 |
+
pip/_vendor/urllib3/util/__pycache__/retry.cpython-314.pyc,,
|
| 865 |
+
pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-314.pyc,,
|
| 866 |
+
pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-314.pyc,,
|
| 867 |
+
pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-314.pyc,,
|
| 868 |
+
pip/_vendor/urllib3/util/__pycache__/timeout.cpython-314.pyc,,
|
| 869 |
+
pip/_vendor/urllib3/util/__pycache__/url.cpython-314.pyc,,
|
| 870 |
+
pip/_vendor/urllib3/util/__pycache__/util.cpython-314.pyc,,
|
| 871 |
+
pip/_vendor/urllib3/util/__pycache__/wait.cpython-314.pyc,,
|
| 872 |
+
pip/_vendor/urllib3/util/connection.py,sha256=JjO722lzHlzLXPTkr9ZWBdhseXnMVjMSb1DJLVrXSnQ,4444
|
| 873 |
+
pip/_vendor/urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148
|
| 874 |
+
pip/_vendor/urllib3/util/request.py,sha256=p9Ki9eo1tFBO-jqV_7KmmJ60RKqoY2r4ao0SmaHLyOs,8086
|
| 875 |
+
pip/_vendor/urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374
|
| 876 |
+
pip/_vendor/urllib3/util/retry.py,sha256=2YnSX-_FecMShD61Mx5s68J0_btUHZrrc_BkFVRS1P4,19577
|
| 877 |
+
pip/_vendor/urllib3/util/ssl_.py,sha256=Oqe3rIhUU3e3GVgZob2hxmR8Q0ZDhrhESPlPP_GLaVQ,17742
|
| 878 |
+
pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ft44KJzTzGMmKff_ZXP91li2V4WhmvEy16PKBcv4vZk,5479
|
| 879 |
+
pip/_vendor/urllib3/util/ssltransport.py,sha256=Ez4O8pR_vT8dan_FvqBYS6dgDfBXEMfVfrzcdUoWfi4,8847
|
| 880 |
+
pip/_vendor/urllib3/util/timeout.py,sha256=vsUJRpO0nfKk-y1OKlgFGY1ONJGPgkaZ7B7kruEpVYw,10363
|
| 881 |
+
pip/_vendor/urllib3/util/url.py,sha256=PEDQMypidude0nAZctLLiFK9epN-LPnSH7KpOLLwqH0,15256
|
| 882 |
+
pip/_vendor/urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146
|
| 883 |
+
pip/_vendor/urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423
|
| 884 |
+
pip/_vendor/vendor.txt,sha256=PfDJYBv6Lg3KI8xXSk-QCk1_2tBgqyA_q4NUNQ7ZMyA,317
|
| 885 |
+
pip/py.typed,sha256=EBVvvPRTn_eIpz5e5QztSCdrMX7Qwd7VP93RSoIlZ2I,286
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/REQUESTED
ADDED
|
File without changes
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: flit 3.12.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
pip=pip._internal.cli.main:main
|
| 3 |
+
pip3=pip._internal.cli.main:main
|
| 4 |
+
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/AUTHORS.txt
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@Switch01
|
| 2 |
+
A_Rog
|
| 3 |
+
Aakanksha Agrawal
|
| 4 |
+
Aarni Koskela
|
| 5 |
+
abdul rawoof
|
| 6 |
+
Abdul Rawoof Khan
|
| 7 |
+
Abhinav Sagar
|
| 8 |
+
ABHYUDAY PRATAP SINGH
|
| 9 |
+
abs51295
|
| 10 |
+
AceGentile
|
| 11 |
+
Adam Chainz
|
| 12 |
+
Adam Tse
|
| 13 |
+
Adam Turner
|
| 14 |
+
Adam Wentz
|
| 15 |
+
admin
|
| 16 |
+
Adolfo Ochagavía
|
| 17 |
+
Adrien Morison
|
| 18 |
+
Agus
|
| 19 |
+
ahayrapetyan
|
| 20 |
+
Ahilya
|
| 21 |
+
AinsworthK
|
| 22 |
+
Akash Srivastava
|
| 23 |
+
Alan Yee
|
| 24 |
+
Albert Tugushev
|
| 25 |
+
Albert-Guan
|
| 26 |
+
albertg
|
| 27 |
+
Alberto Sottile
|
| 28 |
+
Aleks Bunin
|
| 29 |
+
Ales Erjavec
|
| 30 |
+
Alessandro Molina
|
| 31 |
+
Alethea Flowers
|
| 32 |
+
Alex Gaynor
|
| 33 |
+
Alex Grönholm
|
| 34 |
+
Alex Hedges
|
| 35 |
+
Alex Loosley
|
| 36 |
+
Alex Morega
|
| 37 |
+
Alex Stachowiak
|
| 38 |
+
Alexander Regueiro
|
| 39 |
+
Alexander Shtyrov
|
| 40 |
+
Alexandre Conrad
|
| 41 |
+
Alexey Popravka
|
| 42 |
+
Aleš Erjavec
|
| 43 |
+
Alli
|
| 44 |
+
Aman
|
| 45 |
+
Ami Fischman
|
| 46 |
+
Ananya Maiti
|
| 47 |
+
Anatoly Techtonik
|
| 48 |
+
Anders Kaseorg
|
| 49 |
+
Andre Aguiar
|
| 50 |
+
Andrea Corna
|
| 51 |
+
Andreas Lutro
|
| 52 |
+
Andrei Geacar
|
| 53 |
+
Andrew Gaul
|
| 54 |
+
Andrew Shymanel
|
| 55 |
+
Andrey Bienkowski
|
| 56 |
+
Andrey Bulgakov
|
| 57 |
+
Andrés Delfino
|
| 58 |
+
Andy Freeland
|
| 59 |
+
Andy Kluger
|
| 60 |
+
Ani Hayrapetyan
|
| 61 |
+
Aniruddha Basak
|
| 62 |
+
Anish Tambe
|
| 63 |
+
Anrs Hu
|
| 64 |
+
Anthony Sottile
|
| 65 |
+
Antoine Lambert
|
| 66 |
+
Antoine Musso
|
| 67 |
+
Anton Ovchinnikov
|
| 68 |
+
Anton Patrushev
|
| 69 |
+
Anton Zelenov
|
| 70 |
+
Antonio Alvarado Hernandez
|
| 71 |
+
Antony Lee
|
| 72 |
+
Antti Kaihola
|
| 73 |
+
Anubhav Patel
|
| 74 |
+
Anudit Nagar
|
| 75 |
+
Anuj Godase
|
| 76 |
+
AQNOUCH Mohammed
|
| 77 |
+
AraHaan
|
| 78 |
+
Aren Siekmeier
|
| 79 |
+
arena
|
| 80 |
+
arenasys
|
| 81 |
+
Arindam Choudhury
|
| 82 |
+
Armin Ronacher
|
| 83 |
+
Arnon Yaari
|
| 84 |
+
Artem
|
| 85 |
+
Arun Babu Neelicattu
|
| 86 |
+
Ashley Manton
|
| 87 |
+
Ashwin Ramaswami
|
| 88 |
+
atse
|
| 89 |
+
Atsushi Odagiri
|
| 90 |
+
Avinash Karhana
|
| 91 |
+
Avner Cohen
|
| 92 |
+
Awit (Ah-Wit) Ghirmai
|
| 93 |
+
Baptiste Mispelon
|
| 94 |
+
Barney Gale
|
| 95 |
+
barneygale
|
| 96 |
+
Bartek Ogryczak
|
| 97 |
+
Bastian Venthur
|
| 98 |
+
Ben Bodenmiller
|
| 99 |
+
Ben Darnell
|
| 100 |
+
Ben Hoyt
|
| 101 |
+
Ben Mares
|
| 102 |
+
Ben Rosser
|
| 103 |
+
Bence Nagy
|
| 104 |
+
Benjamin Peterson
|
| 105 |
+
Benjamin VanEvery
|
| 106 |
+
Benoit Pierre
|
| 107 |
+
Berker Peksag
|
| 108 |
+
Bernard
|
| 109 |
+
Bernard Tyers
|
| 110 |
+
Bernardo B. Marques
|
| 111 |
+
Bernhard M. Wiedemann
|
| 112 |
+
Bertil Hatt
|
| 113 |
+
Bhavam Vidyarthi
|
| 114 |
+
Blazej Michalik
|
| 115 |
+
Bogdan Opanchuk
|
| 116 |
+
BorisZZZ
|
| 117 |
+
Brad Erickson
|
| 118 |
+
Bradley Ayers
|
| 119 |
+
Bradley Reynolds
|
| 120 |
+
Branch Vincent
|
| 121 |
+
Brandon L. Reiss
|
| 122 |
+
Brandt Bucher
|
| 123 |
+
Brannon Dorsey
|
| 124 |
+
Brett Randall
|
| 125 |
+
Brett Rosen
|
| 126 |
+
Brian Cristante
|
| 127 |
+
Brian Rosner
|
| 128 |
+
briantracy
|
| 129 |
+
BrownTruck
|
| 130 |
+
Bruno Oliveira
|
| 131 |
+
Bruno Renié
|
| 132 |
+
Bruno S
|
| 133 |
+
Bstrdsmkr
|
| 134 |
+
Buck Golemon
|
| 135 |
+
burrows
|
| 136 |
+
Bussonnier Matthias
|
| 137 |
+
bwoodsend
|
| 138 |
+
c22
|
| 139 |
+
Caleb Brown
|
| 140 |
+
Caleb Martinez
|
| 141 |
+
Calvin Smith
|
| 142 |
+
Carl Meyer
|
| 143 |
+
Carlos Liam
|
| 144 |
+
Carol Willing
|
| 145 |
+
Carter Thayer
|
| 146 |
+
Cass
|
| 147 |
+
Chandrasekhar Atina
|
| 148 |
+
Charlie Marsh
|
| 149 |
+
charwick
|
| 150 |
+
Chih-Hsuan Yen
|
| 151 |
+
Chris Brinker
|
| 152 |
+
Chris Hunt
|
| 153 |
+
Chris Jerdonek
|
| 154 |
+
Chris Kuehl
|
| 155 |
+
Chris Markiewicz
|
| 156 |
+
Chris McDonough
|
| 157 |
+
Chris Pawley
|
| 158 |
+
Chris Pryer
|
| 159 |
+
Chris Wolfe
|
| 160 |
+
Christian Clauss
|
| 161 |
+
Christian Heimes
|
| 162 |
+
Christian Oudard
|
| 163 |
+
Christoph Reiter
|
| 164 |
+
Christopher Hunt
|
| 165 |
+
Christopher Snyder
|
| 166 |
+
chrysle
|
| 167 |
+
cjc7373
|
| 168 |
+
Clark Boylan
|
| 169 |
+
Claudio Jolowicz
|
| 170 |
+
Clay McClure
|
| 171 |
+
Cody
|
| 172 |
+
Cody Soyland
|
| 173 |
+
Colin Watson
|
| 174 |
+
Collin Anderson
|
| 175 |
+
Connor Osborn
|
| 176 |
+
Cooper Lees
|
| 177 |
+
Cooper Ry Lees
|
| 178 |
+
Cory Benfield
|
| 179 |
+
Cory Wright
|
| 180 |
+
Craig Kerstiens
|
| 181 |
+
Cristian Sorinel
|
| 182 |
+
Cristina
|
| 183 |
+
Cristina Muñoz
|
| 184 |
+
ctg123
|
| 185 |
+
Curtis Doty
|
| 186 |
+
cytolentino
|
| 187 |
+
Daan De Meyer
|
| 188 |
+
Dale
|
| 189 |
+
Damian
|
| 190 |
+
Damian Quiroga
|
| 191 |
+
Damian Shaw
|
| 192 |
+
Dan Black
|
| 193 |
+
Dan Savilonis
|
| 194 |
+
Dan Sully
|
| 195 |
+
Dane Hillard
|
| 196 |
+
daniel
|
| 197 |
+
Daniel Collins
|
| 198 |
+
Daniel Hahler
|
| 199 |
+
Daniel Hollas
|
| 200 |
+
Daniel Holth
|
| 201 |
+
Daniel Jost
|
| 202 |
+
Daniel Katz
|
| 203 |
+
Daniel Shaulov
|
| 204 |
+
Daniele Esposti
|
| 205 |
+
Daniele Nicolodi
|
| 206 |
+
Daniele Procida
|
| 207 |
+
Daniil Konovalenko
|
| 208 |
+
Danny Hermes
|
| 209 |
+
Danny McClanahan
|
| 210 |
+
Darren Kavanagh
|
| 211 |
+
Dav Clark
|
| 212 |
+
Dave Abrahams
|
| 213 |
+
Dave Jones
|
| 214 |
+
David Aguilar
|
| 215 |
+
David Black
|
| 216 |
+
David Bordeynik
|
| 217 |
+
David Caro
|
| 218 |
+
David D Lowe
|
| 219 |
+
David Evans
|
| 220 |
+
David Hewitt
|
| 221 |
+
David Linke
|
| 222 |
+
David Poggi
|
| 223 |
+
David Poznik
|
| 224 |
+
David Pursehouse
|
| 225 |
+
David Runge
|
| 226 |
+
David Tucker
|
| 227 |
+
David Wales
|
| 228 |
+
Davidovich
|
| 229 |
+
Dayna Blackwell
|
| 230 |
+
ddelange
|
| 231 |
+
Deepak Sharma
|
| 232 |
+
Deepyaman Datta
|
| 233 |
+
Denis Roussel (ACSONE)
|
| 234 |
+
Denise Yu
|
| 235 |
+
dependabot[bot]
|
| 236 |
+
dereksz
|
| 237 |
+
derwolfe
|
| 238 |
+
Desetude
|
| 239 |
+
developer
|
| 240 |
+
Devesh Kumar
|
| 241 |
+
Devesh Kumar Singh
|
| 242 |
+
devsagul
|
| 243 |
+
Dewank Pant
|
| 244 |
+
Diego Caraballo
|
| 245 |
+
Diego Ramirez
|
| 246 |
+
DiegoCaraballo
|
| 247 |
+
Dimitri Merejkowsky
|
| 248 |
+
Dimitri Papadopoulos
|
| 249 |
+
Dimitri Papadopoulos Orfanos
|
| 250 |
+
Dirk Stolle
|
| 251 |
+
dkjsone
|
| 252 |
+
Dmitrii Sutiagin
|
| 253 |
+
Dmitry Gladkov
|
| 254 |
+
Dmitry Volodin
|
| 255 |
+
Domen Kožar
|
| 256 |
+
Dominic Davis-Foster
|
| 257 |
+
Donald Stufft
|
| 258 |
+
Dongweiming
|
| 259 |
+
doron zarhi
|
| 260 |
+
Dos Moonen
|
| 261 |
+
Douglas Thor
|
| 262 |
+
DrFeathers
|
| 263 |
+
Dustin Ingram
|
| 264 |
+
Dustin Rodrigues
|
| 265 |
+
Dwayne Bailey
|
| 266 |
+
Ed Morley
|
| 267 |
+
Edgar Ramírez
|
| 268 |
+
Edgar Ramírez Mondragón
|
| 269 |
+
Ee Durbin
|
| 270 |
+
Efflam Lemaillet
|
| 271 |
+
efflamlemaillet
|
| 272 |
+
Eitan Adler
|
| 273 |
+
ekristina
|
| 274 |
+
elainechan
|
| 275 |
+
Eli Schwartz
|
| 276 |
+
Elisha Hollander
|
| 277 |
+
Ellen Marie Dash
|
| 278 |
+
Emil Burzo
|
| 279 |
+
Emil Styrke
|
| 280 |
+
Emmanuel Arias
|
| 281 |
+
Endoh Takanao
|
| 282 |
+
enoch
|
| 283 |
+
Erdinc Mutlu
|
| 284 |
+
Eric Cousineau
|
| 285 |
+
Eric Gillingham
|
| 286 |
+
Eric Hanchrow
|
| 287 |
+
Eric Hopper
|
| 288 |
+
Erik M. Bray
|
| 289 |
+
Erik Rose
|
| 290 |
+
Erwin Janssen
|
| 291 |
+
Eugene Vereshchagin
|
| 292 |
+
everdimension
|
| 293 |
+
Federico
|
| 294 |
+
Felipe Peter
|
| 295 |
+
Felix Yan
|
| 296 |
+
fiber-space
|
| 297 |
+
Filip Kokosiński
|
| 298 |
+
Filipe Laíns
|
| 299 |
+
Finn Womack
|
| 300 |
+
finnagin
|
| 301 |
+
Flavio Amurrio
|
| 302 |
+
Florian Briand
|
| 303 |
+
Florian Rathgeber
|
| 304 |
+
Francesco
|
| 305 |
+
Francesco Montesano
|
| 306 |
+
Fredrik Orderud
|
| 307 |
+
Fredrik Roubert
|
| 308 |
+
Frost Ming
|
| 309 |
+
Gabriel Curio
|
| 310 |
+
Gabriel de Perthuis
|
| 311 |
+
Garry Polley
|
| 312 |
+
gavin
|
| 313 |
+
gdanielson
|
| 314 |
+
Gene Wood
|
| 315 |
+
Geoffrey Sneddon
|
| 316 |
+
George Margaritis
|
| 317 |
+
George Song
|
| 318 |
+
Georgi Valkov
|
| 319 |
+
Georgy Pchelkin
|
| 320 |
+
Gertjan van Zwieten
|
| 321 |
+
ghost
|
| 322 |
+
Giancarlo Cicellyn Comneno
|
| 323 |
+
Giftlin Rajaiah
|
| 324 |
+
gizmoguy1
|
| 325 |
+
gkdoc
|
| 326 |
+
Godefroid Chapelle
|
| 327 |
+
Gopinath M
|
| 328 |
+
GOTO Hayato
|
| 329 |
+
gousaiyang
|
| 330 |
+
gpiks
|
| 331 |
+
Greg Roodt
|
| 332 |
+
Greg Ward
|
| 333 |
+
Guido Diepen
|
| 334 |
+
Guilherme Espada
|
| 335 |
+
Guillaume Seguin
|
| 336 |
+
gutsytechster
|
| 337 |
+
Guy Rozendorn
|
| 338 |
+
Guy Tuval
|
| 339 |
+
gzpan123
|
| 340 |
+
Hanjun Kim
|
| 341 |
+
Hari Charan
|
| 342 |
+
Harsh Vardhan
|
| 343 |
+
Harsha Sai
|
| 344 |
+
harupy
|
| 345 |
+
Harutaka Kawamura
|
| 346 |
+
Hasan-8326
|
| 347 |
+
hauntsaninja
|
| 348 |
+
Henrich Hartzer
|
| 349 |
+
Henry Schreiner
|
| 350 |
+
Herbert Pfennig
|
| 351 |
+
Holly Stotelmyer
|
| 352 |
+
Honnix
|
| 353 |
+
Hsiaoming Yang
|
| 354 |
+
Hugo
|
| 355 |
+
Hugo Lopes Tavares
|
| 356 |
+
Hugo van Kemenade
|
| 357 |
+
Hugues Bruant
|
| 358 |
+
Hynek Schlawack
|
| 359 |
+
iamsrp-deshaw
|
| 360 |
+
Ian Bicking
|
| 361 |
+
Ian Cordasco
|
| 362 |
+
Ian Lee
|
| 363 |
+
Ian Stapleton Cordasco
|
| 364 |
+
Ian Wienand
|
| 365 |
+
Igor Kuzmitshov
|
| 366 |
+
Igor Sobreira
|
| 367 |
+
Ikko Ashimine
|
| 368 |
+
Ilan Schnell
|
| 369 |
+
Illia Volochii
|
| 370 |
+
Ilya Abdolmanafi
|
| 371 |
+
Ilya Baryshev
|
| 372 |
+
Inada Naoki
|
| 373 |
+
Ionel Cristian Mărieș
|
| 374 |
+
Ionel Maries Cristian
|
| 375 |
+
Itamar Turner-Trauring
|
| 376 |
+
iTrooz
|
| 377 |
+
Ivan Pozdeev
|
| 378 |
+
J. Nick Koston
|
| 379 |
+
Jacob Kim
|
| 380 |
+
Jacob Walls
|
| 381 |
+
Jaime Sanz
|
| 382 |
+
Jake Lishman
|
| 383 |
+
jakirkham
|
| 384 |
+
Jakub Kuczys
|
| 385 |
+
Jakub Stasiak
|
| 386 |
+
Jakub Vysoky
|
| 387 |
+
Jakub Wilk
|
| 388 |
+
James
|
| 389 |
+
James Cleveland
|
| 390 |
+
James Curtin
|
| 391 |
+
James Firth
|
| 392 |
+
James Gerity
|
| 393 |
+
James Polley
|
| 394 |
+
Jan Pokorný
|
| 395 |
+
Jannis Leidel
|
| 396 |
+
Jarek Potiuk
|
| 397 |
+
jarondl
|
| 398 |
+
Jason Curtis
|
| 399 |
+
Jason R. Coombs
|
| 400 |
+
JasonMo
|
| 401 |
+
JasonMo1
|
| 402 |
+
Jay Graves
|
| 403 |
+
Jean Abou Samra
|
| 404 |
+
Jean-Christophe Fillion-Robin
|
| 405 |
+
Jeff Barber
|
| 406 |
+
Jeff Dairiki
|
| 407 |
+
Jeff Widman
|
| 408 |
+
Jelmer Vernooij
|
| 409 |
+
jenix21
|
| 410 |
+
Jeremy Fleischman
|
| 411 |
+
Jeremy Stanley
|
| 412 |
+
Jeremy Zafran
|
| 413 |
+
Jesse Rittner
|
| 414 |
+
Jiashuo Li
|
| 415 |
+
Jim Fisher
|
| 416 |
+
Jim Garrison
|
| 417 |
+
Jinzhe Zeng
|
| 418 |
+
Jiun Bae
|
| 419 |
+
Jivan Amara
|
| 420 |
+
Joa
|
| 421 |
+
Joe Bylund
|
| 422 |
+
Joe Michelini
|
| 423 |
+
Johannes Altmanninger
|
| 424 |
+
John Paton
|
| 425 |
+
John Sirois
|
| 426 |
+
John T. Wodder II
|
| 427 |
+
John-Scott Atlakson
|
| 428 |
+
johnthagen
|
| 429 |
+
Jon Banafato
|
| 430 |
+
Jon Dufresne
|
| 431 |
+
Jon Parise
|
| 432 |
+
Jonas Nockert
|
| 433 |
+
Jonathan Herbert
|
| 434 |
+
Joonatan Partanen
|
| 435 |
+
Joost Molenaar
|
| 436 |
+
Jorge Niedbalski
|
| 437 |
+
Joseph Bylund
|
| 438 |
+
Joseph Long
|
| 439 |
+
Josh Bronson
|
| 440 |
+
Josh Cannon
|
| 441 |
+
Josh Hansen
|
| 442 |
+
Josh Schneier
|
| 443 |
+
Joshua
|
| 444 |
+
JoshuaPerdue
|
| 445 |
+
Jost Migenda
|
| 446 |
+
Juan Luis Cano Rodríguez
|
| 447 |
+
Juanjo Bazán
|
| 448 |
+
Judah Rand
|
| 449 |
+
Julian Berman
|
| 450 |
+
Julian Gethmann
|
| 451 |
+
Julien Demoor
|
| 452 |
+
Julien Stephan
|
| 453 |
+
July Tikhonov
|
| 454 |
+
Jussi Kukkonen
|
| 455 |
+
Justin van Heek
|
| 456 |
+
jwg4
|
| 457 |
+
Jyrki Pulliainen
|
| 458 |
+
Kai Chen
|
| 459 |
+
Kai Mueller
|
| 460 |
+
Kamal Bin Mustafa
|
| 461 |
+
Karolina Surma
|
| 462 |
+
kasium
|
| 463 |
+
kaustav haldar
|
| 464 |
+
Kaz Nishimura
|
| 465 |
+
keanemind
|
| 466 |
+
Keith Maxwell
|
| 467 |
+
Kelsey Hightower
|
| 468 |
+
Kenneth Belitzky
|
| 469 |
+
Kenneth Reitz
|
| 470 |
+
Kevin Burke
|
| 471 |
+
Kevin Carter
|
| 472 |
+
Kevin Frommelt
|
| 473 |
+
Kevin R Patterson
|
| 474 |
+
Kevin Turcios
|
| 475 |
+
Kexuan Sun
|
| 476 |
+
Kit Randel
|
| 477 |
+
Klaas van Schelven
|
| 478 |
+
KOLANICH
|
| 479 |
+
konstin
|
| 480 |
+
kpinc
|
| 481 |
+
Krishan Bhasin
|
| 482 |
+
Krishna Oza
|
| 483 |
+
Kumar McMillan
|
| 484 |
+
Kuntal Majumder
|
| 485 |
+
Kurt McKee
|
| 486 |
+
Kyle Persohn
|
| 487 |
+
lakshmanaram
|
| 488 |
+
Laszlo Kiss-Kollar
|
| 489 |
+
Laurent Bristiel
|
| 490 |
+
Laurent LAPORTE
|
| 491 |
+
Laurie O
|
| 492 |
+
Laurie Opperman
|
| 493 |
+
layday
|
| 494 |
+
Leon Sasson
|
| 495 |
+
Lev Givon
|
| 496 |
+
Lincoln de Sousa
|
| 497 |
+
Lipis
|
| 498 |
+
lorddavidiii
|
| 499 |
+
Loren Carvalho
|
| 500 |
+
Louis Liu
|
| 501 |
+
Lucas Cimon
|
| 502 |
+
Ludovic Gasc
|
| 503 |
+
Luis Carlos
|
| 504 |
+
Luis Medel
|
| 505 |
+
Lukas Geiger
|
| 506 |
+
Lukas Juhrich
|
| 507 |
+
Luke Macken
|
| 508 |
+
Luo Jiebin
|
| 509 |
+
luojiebin
|
| 510 |
+
luz.paz
|
| 511 |
+
László Kiss Kollár
|
| 512 |
+
M00nL1ght
|
| 513 |
+
MajorTanya
|
| 514 |
+
Malcolm Smith
|
| 515 |
+
Marc Abramowitz
|
| 516 |
+
Marc Tamlyn
|
| 517 |
+
Marcus Smith
|
| 518 |
+
Mariatta
|
| 519 |
+
Mark Kohler
|
| 520 |
+
Mark McLoughlin
|
| 521 |
+
Mark Williams
|
| 522 |
+
Markus Hametner
|
| 523 |
+
Martey Dodoo
|
| 524 |
+
Martin Fischer
|
| 525 |
+
Martin Häcker
|
| 526 |
+
Martin Pavlasek
|
| 527 |
+
Masaki
|
| 528 |
+
Masklinn
|
| 529 |
+
Matej Stuchlik
|
| 530 |
+
Mateusz Sokół
|
| 531 |
+
Mathew Jennings
|
| 532 |
+
Mathieu Bridon
|
| 533 |
+
Mathieu Kniewallner
|
| 534 |
+
Matt Bacchi
|
| 535 |
+
Matt Good
|
| 536 |
+
Matt Maker
|
| 537 |
+
Matt Robenolt
|
| 538 |
+
Matt Wozniski
|
| 539 |
+
matthew
|
| 540 |
+
Matthew Einhorn
|
| 541 |
+
Matthew Feickert
|
| 542 |
+
Matthew Gilliard
|
| 543 |
+
Matthew Hughes
|
| 544 |
+
Matthew Iversen
|
| 545 |
+
Matthew Treinish
|
| 546 |
+
Matthew Trumbell
|
| 547 |
+
Matthew Willson
|
| 548 |
+
Matthias Bussonnier
|
| 549 |
+
mattip
|
| 550 |
+
Maurits van Rees
|
| 551 |
+
Max W Chase
|
| 552 |
+
Maxim Kurnikov
|
| 553 |
+
Maxime Rouyrre
|
| 554 |
+
mayeut
|
| 555 |
+
mbaluna
|
| 556 |
+
Md Sujauddin Sekh
|
| 557 |
+
mdebi
|
| 558 |
+
Meet Vasita
|
| 559 |
+
memoselyk
|
| 560 |
+
meowmeowcat
|
| 561 |
+
Michael
|
| 562 |
+
Michael Aquilina
|
| 563 |
+
Michael E. Karpeles
|
| 564 |
+
Michael Klich
|
| 565 |
+
Michael Mintz
|
| 566 |
+
Michael Williamson
|
| 567 |
+
michaelpacer
|
| 568 |
+
Michał Górny
|
| 569 |
+
Mickaël Schoentgen
|
| 570 |
+
Miguel Araujo Perez
|
| 571 |
+
Mihir Singh
|
| 572 |
+
Mike
|
| 573 |
+
Mike Hendricks
|
| 574 |
+
Min RK
|
| 575 |
+
MinRK
|
| 576 |
+
Miro Hrončok
|
| 577 |
+
Mohsen Fooladnezhad
|
| 578 |
+
Monica Baluna
|
| 579 |
+
montefra
|
| 580 |
+
Monty Taylor
|
| 581 |
+
morotti
|
| 582 |
+
mrKazzila
|
| 583 |
+
Muha Ajjan
|
| 584 |
+
MUTHUSRIHEMADHARSHINI S A
|
| 585 |
+
Nadav Wexler
|
| 586 |
+
Nahuel Ambrosini
|
| 587 |
+
Nate Coraor
|
| 588 |
+
Nate Prewitt
|
| 589 |
+
Nathan Houghton
|
| 590 |
+
Nathaniel J. Smith
|
| 591 |
+
Nehal J Wani
|
| 592 |
+
Neil Botelho
|
| 593 |
+
Nguyễn Gia Phong
|
| 594 |
+
Nicholas Serra
|
| 595 |
+
Nick Coghlan
|
| 596 |
+
Nick Stenning
|
| 597 |
+
Nick Timkovich
|
| 598 |
+
Nicolas Bock
|
| 599 |
+
Nicole Harris
|
| 600 |
+
Nikhil Benesch
|
| 601 |
+
Nikhil Ladha
|
| 602 |
+
Nikita Chepanov
|
| 603 |
+
Nikolay Korolev
|
| 604 |
+
Nipunn Koorapati
|
| 605 |
+
Nitesh Sharma
|
| 606 |
+
Niyas Sait
|
| 607 |
+
Noah
|
| 608 |
+
Noah Gorny
|
| 609 |
+
Nok Lam Chan
|
| 610 |
+
Norbert Manthey
|
| 611 |
+
Nothing-991
|
| 612 |
+
Nowell Strite
|
| 613 |
+
NtaleGrey
|
| 614 |
+
nucccc
|
| 615 |
+
nvdv
|
| 616 |
+
OBITORASU
|
| 617 |
+
Ofek Lev
|
| 618 |
+
ofrinevo
|
| 619 |
+
Oleg Burnaev
|
| 620 |
+
Oliver Freund
|
| 621 |
+
Oliver Jeeves
|
| 622 |
+
Oliver Mannion
|
| 623 |
+
Oliver Tonnhofer
|
| 624 |
+
Olivier Girardot
|
| 625 |
+
Olivier Grisel
|
| 626 |
+
Ollie Rutherfurd
|
| 627 |
+
Omar Abdalgwad
|
| 628 |
+
OMOTO Kenji
|
| 629 |
+
Omry Yadan
|
| 630 |
+
onlinejudge95
|
| 631 |
+
Oren Held
|
| 632 |
+
Oscar Benjamin
|
| 633 |
+
oxygen dioxide
|
| 634 |
+
Oz N Tiram
|
| 635 |
+
Pachwenko
|
| 636 |
+
Paresh Joshi
|
| 637 |
+
Patrick Dubroy
|
| 638 |
+
Patrick Jenkins
|
| 639 |
+
Patrick Lawson
|
| 640 |
+
patricktokeeffe
|
| 641 |
+
Patrik Kopkan
|
| 642 |
+
Paul Ganssle
|
| 643 |
+
Paul Kehrer
|
| 644 |
+
Paul Moore
|
| 645 |
+
Paul Nasrat
|
| 646 |
+
Paul Oswald
|
| 647 |
+
Paul van der Linden
|
| 648 |
+
Paulus Schoutsen
|
| 649 |
+
Pavel Safronov
|
| 650 |
+
Pavithra Eswaramoorthy
|
| 651 |
+
Pawel Jasinski
|
| 652 |
+
Paweł Szramowski
|
| 653 |
+
Pekka Klärck
|
| 654 |
+
Peter Gessler
|
| 655 |
+
Peter Lisák
|
| 656 |
+
Peter Shen
|
| 657 |
+
Peter Waller
|
| 658 |
+
Petr Viktorin
|
| 659 |
+
petr-tik
|
| 660 |
+
Phaneendra Chiruvella
|
| 661 |
+
Phil Elson
|
| 662 |
+
Phil Freo
|
| 663 |
+
Phil Pennock
|
| 664 |
+
Phil Whelan
|
| 665 |
+
Philip Jägenstedt
|
| 666 |
+
Philip Molloy
|
| 667 |
+
Philippe Ombredanne
|
| 668 |
+
Pi Delport
|
| 669 |
+
Pierre-Yves Rofes
|
| 670 |
+
Pieter Degroote
|
| 671 |
+
pip
|
| 672 |
+
Prabakaran Kumaresshan
|
| 673 |
+
Prabhjyotsing Surjit Singh Sodhi
|
| 674 |
+
Prabhu Marappan
|
| 675 |
+
Pradyun Gedam
|
| 676 |
+
Prashant Sharma
|
| 677 |
+
Pratik Mallya
|
| 678 |
+
pre-commit-ci[bot]
|
| 679 |
+
Preet Thakkar
|
| 680 |
+
Preston Holmes
|
| 681 |
+
Przemek Wrzos
|
| 682 |
+
Pulkit Goyal
|
| 683 |
+
q0w
|
| 684 |
+
Qiangning Hong
|
| 685 |
+
Qiming Xu
|
| 686 |
+
qraqras
|
| 687 |
+
Quentin Lee
|
| 688 |
+
Quentin Pradet
|
| 689 |
+
R. David Murray
|
| 690 |
+
Rafael Caricio
|
| 691 |
+
RAHUL KUMAR
|
| 692 |
+
rahulkotargasthi
|
| 693 |
+
Ralf Schmitt
|
| 694 |
+
Ran Benita
|
| 695 |
+
Randy Döring
|
| 696 |
+
Razzi Abuissa
|
| 697 |
+
rdb
|
| 698 |
+
Reece Dunham
|
| 699 |
+
Remi Rampin
|
| 700 |
+
Rene Dudfield
|
| 701 |
+
Riccardo Magliocchetti
|
| 702 |
+
Riccardo Schirone
|
| 703 |
+
Richard Jones
|
| 704 |
+
Richard Si
|
| 705 |
+
Ricky Ng-Adam
|
| 706 |
+
Rishi
|
| 707 |
+
rmorotti
|
| 708 |
+
RobberPhex
|
| 709 |
+
Robert Collins
|
| 710 |
+
Robert McGibbon
|
| 711 |
+
Robert Pollak
|
| 712 |
+
Robert T. McGibbon
|
| 713 |
+
robin elisha robinson
|
| 714 |
+
Rodney, Tiara
|
| 715 |
+
Roey Berman
|
| 716 |
+
Rohan Jain
|
| 717 |
+
Roman Bogorodskiy
|
| 718 |
+
Roman Donchenko
|
| 719 |
+
Romuald Brunet
|
| 720 |
+
ronaudinho
|
| 721 |
+
Ronny Pfannschmidt
|
| 722 |
+
Rory McCann
|
| 723 |
+
Ross Brattain
|
| 724 |
+
Roy Wellington Ⅳ
|
| 725 |
+
RoyS
|
| 726 |
+
Ruairidh MacLeod
|
| 727 |
+
Russell Keith-Magee
|
| 728 |
+
Ryan Shepherd
|
| 729 |
+
Ryan Wooden
|
| 730 |
+
ryneeverett
|
| 731 |
+
Ryuma Asai
|
| 732 |
+
S. Guliaev
|
| 733 |
+
Sachi King
|
| 734 |
+
Sai Sridhar Tarra
|
| 735 |
+
Salvatore Rinchiera
|
| 736 |
+
sandeepkiran-js
|
| 737 |
+
Sander Van Balen
|
| 738 |
+
Savio Jomton
|
| 739 |
+
schlamar
|
| 740 |
+
Scott Kitterman
|
| 741 |
+
Sean
|
| 742 |
+
seanj
|
| 743 |
+
Sebastian Höffner
|
| 744 |
+
Sebastian Jordan
|
| 745 |
+
Sebastian Schaetz
|
| 746 |
+
Segev Finer
|
| 747 |
+
SeongSoo Cho
|
| 748 |
+
Sepehr Rasouli
|
| 749 |
+
sepehrrasooli
|
| 750 |
+
Sergey Vasilyev
|
| 751 |
+
Seth Michael Larson
|
| 752 |
+
Seth Woodworth
|
| 753 |
+
Shahar Epstein
|
| 754 |
+
Shantanu
|
| 755 |
+
shenxianpeng
|
| 756 |
+
shireenrao
|
| 757 |
+
Shivam Shrey
|
| 758 |
+
Shivansh-007
|
| 759 |
+
Shixian Sheng
|
| 760 |
+
Shlomi Fish
|
| 761 |
+
Shovan Maity
|
| 762 |
+
Shubham Nagure
|
| 763 |
+
Simeon Visser
|
| 764 |
+
Simon Cross
|
| 765 |
+
Simon Pichugin
|
| 766 |
+
sinoroc
|
| 767 |
+
sinscary
|
| 768 |
+
snook92
|
| 769 |
+
socketubs
|
| 770 |
+
Sorin Sbarnea
|
| 771 |
+
Srinivas Nyayapati
|
| 772 |
+
Srishti Hegde
|
| 773 |
+
Stavros Korokithakis
|
| 774 |
+
Stefan Scherfke
|
| 775 |
+
Stefano Rivera
|
| 776 |
+
Stephan Erb
|
| 777 |
+
Stephane Chazelas
|
| 778 |
+
Stephen Payne
|
| 779 |
+
Stephen Rosen
|
| 780 |
+
stepshal
|
| 781 |
+
Steve (Gadget) Barnes
|
| 782 |
+
Steve Barnes
|
| 783 |
+
Steve Dower
|
| 784 |
+
Steve Kowalik
|
| 785 |
+
Steven Myint
|
| 786 |
+
Steven Silvester
|
| 787 |
+
stonebig
|
| 788 |
+
studioj
|
| 789 |
+
Stéphane Bidoul
|
| 790 |
+
Stéphane Bidoul (ACSONE)
|
| 791 |
+
Stéphane Klein
|
| 792 |
+
Sumana Harihareswara
|
| 793 |
+
Surbhi Sharma
|
| 794 |
+
Sviatoslav Sydorenko
|
| 795 |
+
Sviatoslav Sydorenko (Святослав Сидоренко)
|
| 796 |
+
Swat009
|
| 797 |
+
Sylvain
|
| 798 |
+
Sage Abdullah
|
| 799 |
+
Takayuki SHIMIZUKAWA
|
| 800 |
+
Taneli Hukkinen
|
| 801 |
+
tbeswick
|
| 802 |
+
Terrance
|
| 803 |
+
Thiago
|
| 804 |
+
Thijs Triemstra
|
| 805 |
+
Thomas Fenzl
|
| 806 |
+
Thomas Grainger
|
| 807 |
+
Thomas Guettler
|
| 808 |
+
Thomas Johansson
|
| 809 |
+
Thomas Kluyver
|
| 810 |
+
Thomas Smith
|
| 811 |
+
Thomas VINCENT
|
| 812 |
+
Tim D. Smith
|
| 813 |
+
Tim Gates
|
| 814 |
+
Tim Harder
|
| 815 |
+
Tim Heap
|
| 816 |
+
tim smith
|
| 817 |
+
tinruufu
|
| 818 |
+
Tobias Hermann
|
| 819 |
+
Tom Forbes
|
| 820 |
+
Tom Freudenheim
|
| 821 |
+
Tom V
|
| 822 |
+
Tomas Hrnciar
|
| 823 |
+
Tomas Orsava
|
| 824 |
+
Tomer Chachamu
|
| 825 |
+
Tommi Enenkel | AnB
|
| 826 |
+
Tomáš Hrnčiar
|
| 827 |
+
Tony Beswick
|
| 828 |
+
Tony Narlock
|
| 829 |
+
Tony Zhaocheng Tan
|
| 830 |
+
TonyBeswick
|
| 831 |
+
toonarmycaptain
|
| 832 |
+
Toshio Kuratomi
|
| 833 |
+
toxinu
|
| 834 |
+
Travis Swicegood
|
| 835 |
+
Tushar Sadhwani
|
| 836 |
+
Tzu-ping Chung
|
| 837 |
+
Valentin Haenel
|
| 838 |
+
Victor Stinner
|
| 839 |
+
victorvpaulo
|
| 840 |
+
Vikram - Google
|
| 841 |
+
Viktor Szépe
|
| 842 |
+
Ville Skyttä
|
| 843 |
+
Vinay Sajip
|
| 844 |
+
Vincent Fazio
|
| 845 |
+
Vincent Philippon
|
| 846 |
+
Vinicyus Macedo
|
| 847 |
+
Vipul Kumar
|
| 848 |
+
Vitaly Babiy
|
| 849 |
+
Vladimir Fokow
|
| 850 |
+
Vladimir Rutsky
|
| 851 |
+
W. Trevor King
|
| 852 |
+
Weida Hong
|
| 853 |
+
Wil Tan
|
| 854 |
+
Wilfred Hughes
|
| 855 |
+
William Edwards
|
| 856 |
+
William ML Leslie
|
| 857 |
+
William T Olson
|
| 858 |
+
William Woodruff
|
| 859 |
+
Wilson Mo
|
| 860 |
+
wim glenn
|
| 861 |
+
Winson Luk
|
| 862 |
+
Wolfgang Maier
|
| 863 |
+
Wu Zhenyu
|
| 864 |
+
XAMES3
|
| 865 |
+
Xavier Fernandez
|
| 866 |
+
Xianpeng Shen
|
| 867 |
+
xoviat
|
| 868 |
+
xtreak
|
| 869 |
+
YAMAMOTO Takashi
|
| 870 |
+
Yash
|
| 871 |
+
Yashraj
|
| 872 |
+
Yen Chi Hsuan
|
| 873 |
+
Yeray Diaz Diaz
|
| 874 |
+
Youngkwang Yang
|
| 875 |
+
Yoval P
|
| 876 |
+
Yu Jian
|
| 877 |
+
Yuan Jing Vincent Yan
|
| 878 |
+
Yuki Kobayashi
|
| 879 |
+
Yusuke Hayashi
|
| 880 |
+
Zachary Ware
|
| 881 |
+
zackzack38
|
| 882 |
+
Zearin
|
| 883 |
+
Zhiping Deng
|
| 884 |
+
ziebam
|
| 885 |
+
Zvezdan Petkovic
|
| 886 |
+
Łukasz Langa
|
| 887 |
+
Роман Донченко
|
| 888 |
+
Семён Марьясин
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
|
| 2 |
+
|
| 3 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 4 |
+
a copy of this software and associated documentation files (the
|
| 5 |
+
"Software"), to deal in the Software without restriction, including
|
| 6 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
| 7 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
| 8 |
+
permit persons to whom the Software is furnished to do so, subject to
|
| 9 |
+
the following conditions:
|
| 10 |
+
|
| 11 |
+
The above copyright notice and this permission notice shall be
|
| 12 |
+
included in all copies or substantial portions of the Software.
|
| 13 |
+
|
| 14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 15 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 16 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| 17 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| 18 |
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| 19 |
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| 20 |
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright 2012-2021 Eric Larson
|
| 2 |
+
|
| 3 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
you may not use this file except in compliance with the License.
|
| 5 |
+
You may obtain a copy of the License at
|
| 6 |
+
|
| 7 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
|
| 9 |
+
Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
See the License for the specific language governing permissions and
|
| 13 |
+
limitations under the License.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
This package contains a modified version of ca-bundle.crt:
|
| 2 |
+
|
| 3 |
+
ca-bundle.crt -- Bundle of CA Root Certificates
|
| 4 |
+
|
| 5 |
+
This is a bundle of X.509 certificates of public Certificate Authorities
|
| 6 |
+
(CA). These were automatically extracted from Mozilla's root certificates
|
| 7 |
+
file (certdata.txt). This file can be found in the mozilla source tree:
|
| 8 |
+
https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt
|
| 9 |
+
It contains the certificates in PEM format and therefore
|
| 10 |
+
can be directly used with curl / libcurl / php_curl, or with
|
| 11 |
+
an Apache+mod_ssl webserver for SSL client authentication.
|
| 12 |
+
Just configure this file as the SSLCACertificateFile.#
|
| 13 |
+
|
| 14 |
+
***** BEGIN LICENSE BLOCK *****
|
| 15 |
+
This Source Code Form is subject to the terms of the Mozilla Public License,
|
| 16 |
+
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
|
| 17 |
+
one at http://mozilla.org/MPL/2.0/.
|
| 18 |
+
|
| 19 |
+
***** END LICENSE BLOCK *****
|
| 20 |
+
@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
A. HISTORY OF THE SOFTWARE
|
| 2 |
+
==========================
|
| 3 |
+
|
| 4 |
+
Python was created in the early 1990s by Guido van Rossum at Stichting
|
| 5 |
+
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
| 6 |
+
as a successor of a language called ABC. Guido remains Python's
|
| 7 |
+
principal author, although it includes many contributions from others.
|
| 8 |
+
|
| 9 |
+
In 1995, Guido continued his work on Python at the Corporation for
|
| 10 |
+
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
| 11 |
+
in Reston, Virginia where he released several versions of the
|
| 12 |
+
software.
|
| 13 |
+
|
| 14 |
+
In May 2000, Guido and the Python core development team moved to
|
| 15 |
+
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
| 16 |
+
year, the PythonLabs team moved to Digital Creations (now Zope
|
| 17 |
+
Corporation, see http://www.zope.com). In 2001, the Python Software
|
| 18 |
+
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
| 19 |
+
non-profit organization created specifically to own Python-related
|
| 20 |
+
Intellectual Property. Zope Corporation is a sponsoring member of
|
| 21 |
+
the PSF.
|
| 22 |
+
|
| 23 |
+
All Python releases are Open Source (see http://www.opensource.org for
|
| 24 |
+
the Open Source Definition). Historically, most, but not all, Python
|
| 25 |
+
releases have also been GPL-compatible; the table below summarizes
|
| 26 |
+
the various releases.
|
| 27 |
+
|
| 28 |
+
Release Derived Year Owner GPL-
|
| 29 |
+
from compatible? (1)
|
| 30 |
+
|
| 31 |
+
0.9.0 thru 1.2 1991-1995 CWI yes
|
| 32 |
+
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
| 33 |
+
1.6 1.5.2 2000 CNRI no
|
| 34 |
+
2.0 1.6 2000 BeOpen.com no
|
| 35 |
+
1.6.1 1.6 2001 CNRI yes (2)
|
| 36 |
+
2.1 2.0+1.6.1 2001 PSF no
|
| 37 |
+
2.0.1 2.0+1.6.1 2001 PSF yes
|
| 38 |
+
2.1.1 2.1+2.0.1 2001 PSF yes
|
| 39 |
+
2.2 2.1.1 2001 PSF yes
|
| 40 |
+
2.1.2 2.1.1 2002 PSF yes
|
| 41 |
+
2.1.3 2.1.2 2002 PSF yes
|
| 42 |
+
2.2.1 2.2 2002 PSF yes
|
| 43 |
+
2.2.2 2.2.1 2002 PSF yes
|
| 44 |
+
2.2.3 2.2.2 2003 PSF yes
|
| 45 |
+
2.3 2.2.2 2002-2003 PSF yes
|
| 46 |
+
2.3.1 2.3 2002-2003 PSF yes
|
| 47 |
+
2.3.2 2.3.1 2002-2003 PSF yes
|
| 48 |
+
2.3.3 2.3.2 2002-2003 PSF yes
|
| 49 |
+
2.3.4 2.3.3 2004 PSF yes
|
| 50 |
+
2.3.5 2.3.4 2005 PSF yes
|
| 51 |
+
2.4 2.3 2004 PSF yes
|
| 52 |
+
2.4.1 2.4 2005 PSF yes
|
| 53 |
+
2.4.2 2.4.1 2005 PSF yes
|
| 54 |
+
2.4.3 2.4.2 2006 PSF yes
|
| 55 |
+
2.4.4 2.4.3 2006 PSF yes
|
| 56 |
+
2.5 2.4 2006 PSF yes
|
| 57 |
+
2.5.1 2.5 2007 PSF yes
|
| 58 |
+
2.5.2 2.5.1 2008 PSF yes
|
| 59 |
+
2.5.3 2.5.2 2008 PSF yes
|
| 60 |
+
2.6 2.5 2008 PSF yes
|
| 61 |
+
2.6.1 2.6 2008 PSF yes
|
| 62 |
+
2.6.2 2.6.1 2009 PSF yes
|
| 63 |
+
2.6.3 2.6.2 2009 PSF yes
|
| 64 |
+
2.6.4 2.6.3 2009 PSF yes
|
| 65 |
+
2.6.5 2.6.4 2010 PSF yes
|
| 66 |
+
3.0 2.6 2008 PSF yes
|
| 67 |
+
3.0.1 3.0 2009 PSF yes
|
| 68 |
+
3.1 3.0.1 2009 PSF yes
|
| 69 |
+
3.1.1 3.1 2009 PSF yes
|
| 70 |
+
3.1.2 3.1 2010 PSF yes
|
| 71 |
+
3.2 3.1 2010 PSF yes
|
| 72 |
+
|
| 73 |
+
Footnotes:
|
| 74 |
+
|
| 75 |
+
(1) GPL-compatible doesn't mean that we're distributing Python under
|
| 76 |
+
the GPL. All Python licenses, unlike the GPL, let you distribute
|
| 77 |
+
a modified version without making your changes open source. The
|
| 78 |
+
GPL-compatible licenses make it possible to combine Python with
|
| 79 |
+
other software that is released under the GPL; the others don't.
|
| 80 |
+
|
| 81 |
+
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
| 82 |
+
because its license has a choice of law clause. According to
|
| 83 |
+
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
| 84 |
+
is "not incompatible" with the GPL.
|
| 85 |
+
|
| 86 |
+
Thanks to the many outside volunteers who have worked under Guido's
|
| 87 |
+
direction to make these releases possible.
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
| 91 |
+
===============================================================
|
| 92 |
+
|
| 93 |
+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
| 94 |
+
--------------------------------------------
|
| 95 |
+
|
| 96 |
+
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
| 97 |
+
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
| 98 |
+
otherwise using this software ("Python") in source or binary form and
|
| 99 |
+
its associated documentation.
|
| 100 |
+
|
| 101 |
+
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
| 102 |
+
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
| 103 |
+
analyze, test, perform and/or display publicly, prepare derivative works,
|
| 104 |
+
distribute, and otherwise use Python alone or in any derivative version,
|
| 105 |
+
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
| 106 |
+
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
| 107 |
+
Python Software Foundation; All Rights Reserved" are retained in Python alone or
|
| 108 |
+
in any derivative version prepared by Licensee.
|
| 109 |
+
|
| 110 |
+
3. In the event Licensee prepares a derivative work that is based on
|
| 111 |
+
or incorporates Python or any part thereof, and wants to make
|
| 112 |
+
the derivative work available to others as provided herein, then
|
| 113 |
+
Licensee hereby agrees to include in any such work a brief summary of
|
| 114 |
+
the changes made to Python.
|
| 115 |
+
|
| 116 |
+
4. PSF is making Python available to Licensee on an "AS IS"
|
| 117 |
+
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
| 118 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
| 119 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
| 120 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
| 121 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
| 122 |
+
|
| 123 |
+
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
| 124 |
+
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
| 125 |
+
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
| 126 |
+
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
| 127 |
+
|
| 128 |
+
6. This License Agreement will automatically terminate upon a material
|
| 129 |
+
breach of its terms and conditions.
|
| 130 |
+
|
| 131 |
+
7. Nothing in this License Agreement shall be deemed to create any
|
| 132 |
+
relationship of agency, partnership, or joint venture between PSF and
|
| 133 |
+
Licensee. This License Agreement does not grant permission to use PSF
|
| 134 |
+
trademarks or trade name in a trademark sense to endorse or promote
|
| 135 |
+
products or services of Licensee, or any third party.
|
| 136 |
+
|
| 137 |
+
8. By copying, installing or otherwise using Python, Licensee
|
| 138 |
+
agrees to be bound by the terms and conditions of this License
|
| 139 |
+
Agreement.
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
| 143 |
+
-------------------------------------------
|
| 144 |
+
|
| 145 |
+
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
| 146 |
+
|
| 147 |
+
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
| 148 |
+
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
| 149 |
+
Individual or Organization ("Licensee") accessing and otherwise using
|
| 150 |
+
this software in source or binary form and its associated
|
| 151 |
+
documentation ("the Software").
|
| 152 |
+
|
| 153 |
+
2. Subject to the terms and conditions of this BeOpen Python License
|
| 154 |
+
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
| 155 |
+
royalty-free, world-wide license to reproduce, analyze, test, perform
|
| 156 |
+
and/or display publicly, prepare derivative works, distribute, and
|
| 157 |
+
otherwise use the Software alone or in any derivative version,
|
| 158 |
+
provided, however, that the BeOpen Python License is retained in the
|
| 159 |
+
Software, alone or in any derivative version prepared by Licensee.
|
| 160 |
+
|
| 161 |
+
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
| 162 |
+
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
| 163 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
| 164 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
| 165 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
| 166 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
| 167 |
+
|
| 168 |
+
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
| 169 |
+
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
| 170 |
+
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
| 171 |
+
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
| 172 |
+
|
| 173 |
+
5. This License Agreement will automatically terminate upon a material
|
| 174 |
+
breach of its terms and conditions.
|
| 175 |
+
|
| 176 |
+
6. This License Agreement shall be governed by and interpreted in all
|
| 177 |
+
respects by the law of the State of California, excluding conflict of
|
| 178 |
+
law provisions. Nothing in this License Agreement shall be deemed to
|
| 179 |
+
create any relationship of agency, partnership, or joint venture
|
| 180 |
+
between BeOpen and Licensee. This License Agreement does not grant
|
| 181 |
+
permission to use BeOpen trademarks or trade names in a trademark
|
| 182 |
+
sense to endorse or promote products or services of Licensee, or any
|
| 183 |
+
third party. As an exception, the "BeOpen Python" logos available at
|
| 184 |
+
http://www.pythonlabs.com/logos.html may be used according to the
|
| 185 |
+
permissions granted on that web page.
|
| 186 |
+
|
| 187 |
+
7. By copying, installing or otherwise using the software, Licensee
|
| 188 |
+
agrees to be bound by the terms and conditions of this License
|
| 189 |
+
Agreement.
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
| 193 |
+
---------------------------------------
|
| 194 |
+
|
| 195 |
+
1. This LICENSE AGREEMENT is between the Corporation for National
|
| 196 |
+
Research Initiatives, having an office at 1895 Preston White Drive,
|
| 197 |
+
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
| 198 |
+
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
| 199 |
+
source or binary form and its associated documentation.
|
| 200 |
+
|
| 201 |
+
2. Subject to the terms and conditions of this License Agreement, CNRI
|
| 202 |
+
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
| 203 |
+
license to reproduce, analyze, test, perform and/or display publicly,
|
| 204 |
+
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
| 205 |
+
alone or in any derivative version, provided, however, that CNRI's
|
| 206 |
+
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
| 207 |
+
1995-2001 Corporation for National Research Initiatives; All Rights
|
| 208 |
+
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
| 209 |
+
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
| 210 |
+
Agreement, Licensee may substitute the following text (omitting the
|
| 211 |
+
quotes): "Python 1.6.1 is made available subject to the terms and
|
| 212 |
+
conditions in CNRI's License Agreement. This Agreement together with
|
| 213 |
+
Python 1.6.1 may be located on the Internet using the following
|
| 214 |
+
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
| 215 |
+
Agreement may also be obtained from a proxy server on the Internet
|
| 216 |
+
using the following URL: http://hdl.handle.net/1895.22/1013".
|
| 217 |
+
|
| 218 |
+
3. In the event Licensee prepares a derivative work that is based on
|
| 219 |
+
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
| 220 |
+
the derivative work available to others as provided herein, then
|
| 221 |
+
Licensee hereby agrees to include in any such work a brief summary of
|
| 222 |
+
the changes made to Python 1.6.1.
|
| 223 |
+
|
| 224 |
+
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
| 225 |
+
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
| 226 |
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
| 227 |
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
| 228 |
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
| 229 |
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
| 230 |
+
|
| 231 |
+
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
| 232 |
+
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
| 233 |
+
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
| 234 |
+
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
| 235 |
+
|
| 236 |
+
6. This License Agreement will automatically terminate upon a material
|
| 237 |
+
breach of its terms and conditions.
|
| 238 |
+
|
| 239 |
+
7. This License Agreement shall be governed by the federal
|
| 240 |
+
intellectual property law of the United States, including without
|
| 241 |
+
limitation the federal copyright law, and, to the extent such
|
| 242 |
+
U.S. federal law does not apply, by the law of the Commonwealth of
|
| 243 |
+
Virginia, excluding Virginia's conflict of law provisions.
|
| 244 |
+
Notwithstanding the foregoing, with regard to derivative works based
|
| 245 |
+
on Python 1.6.1 that incorporate non-separable material that was
|
| 246 |
+
previously distributed under the GNU General Public License (GPL), the
|
| 247 |
+
law of the Commonwealth of Virginia shall govern this License
|
| 248 |
+
Agreement only as to issues arising under or with respect to
|
| 249 |
+
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
| 250 |
+
License Agreement shall be deemed to create any relationship of
|
| 251 |
+
agency, partnership, or joint venture between CNRI and Licensee. This
|
| 252 |
+
License Agreement does not grant permission to use CNRI trademarks or
|
| 253 |
+
trade name in a trademark sense to endorse or promote products or
|
| 254 |
+
services of Licensee, or any third party.
|
| 255 |
+
|
| 256 |
+
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
| 257 |
+
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
| 258 |
+
bound by the terms and conditions of this License Agreement.
|
| 259 |
+
|
| 260 |
+
ACCEPT
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
| 264 |
+
--------------------------------------------------
|
| 265 |
+
|
| 266 |
+
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
| 267 |
+
The Netherlands. All rights reserved.
|
| 268 |
+
|
| 269 |
+
Permission to use, copy, modify, and distribute this software and its
|
| 270 |
+
documentation for any purpose and without fee is hereby granted,
|
| 271 |
+
provided that the above copyright notice appear in all copies and that
|
| 272 |
+
both that copyright notice and this permission notice appear in
|
| 273 |
+
supporting documentation, and that the name of Stichting Mathematisch
|
| 274 |
+
Centrum or CWI not be used in advertising or publicity pertaining to
|
| 275 |
+
distribution of the software without specific, written prior
|
| 276 |
+
permission.
|
| 277 |
+
|
| 278 |
+
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
| 279 |
+
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
| 280 |
+
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
| 281 |
+
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
| 282 |
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
| 283 |
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
| 284 |
+
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright {yyyy} {name of copyright owner}
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
| 202 |
+
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BSD 3-Clause License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2013-2026, Kim Davies and contributors.
|
| 4 |
+
All rights reserved.
|
| 5 |
+
|
| 6 |
+
Redistribution and use in source and binary forms, with or without
|
| 7 |
+
modification, are permitted provided that the following conditions are
|
| 8 |
+
met:
|
| 9 |
+
|
| 10 |
+
1. Redistributions of source code must retain the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer.
|
| 12 |
+
|
| 13 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 14 |
+
notice, this list of conditions and the following disclaimer in the
|
| 15 |
+
documentation and/or other materials provided with the distribution.
|
| 16 |
+
|
| 17 |
+
3. Neither the name of the copyright holder nor the names of its
|
| 18 |
+
contributors may be used to endorse or promote products derived from
|
| 19 |
+
this software without specific prior written permission.
|
| 20 |
+
|
| 21 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 24 |
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 25 |
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 26 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
| 27 |
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| 28 |
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 29 |
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 30 |
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 31 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (C) 2008-2011 INADA Naoki <songofacandy@gmail.com>
|
| 2 |
+
|
| 3 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
you may not use this file except in compliance with the License.
|
| 5 |
+
You may obtain a copy of the License at
|
| 6 |
+
|
| 7 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
|
| 9 |
+
Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
See the License for the specific language governing permissions and
|
| 13 |
+
limitations under the License.
|
| 14 |
+
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
This software is made available under the terms of *either* of the licenses
|
| 2 |
+
found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
|
| 3 |
+
under the terms of *both* these licenses.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) Donald Stufft and individual contributors.
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without
|
| 5 |
+
modification, are permitted provided that the following conditions are met:
|
| 6 |
+
|
| 7 |
+
1. Redistributions of source code must retain the above copyright notice,
|
| 8 |
+
this list of conditions and the following disclaimer.
|
| 9 |
+
|
| 10 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer in the
|
| 12 |
+
documentation and/or other materials provided with the distribution.
|
| 13 |
+
|
| 14 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 15 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 16 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 17 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
| 18 |
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| 19 |
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| 20 |
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 21 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
| 22 |
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 23 |
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 2 |
+
of this software and associated documentation files (the "Software"), to
|
| 3 |
+
deal in the Software without restriction, including without limitation the
|
| 4 |
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
| 5 |
+
sell copies of the Software, and to permit persons to whom the Software is
|
| 6 |
+
furnished to do so, subject to the following conditions:
|
| 7 |
+
|
| 8 |
+
The above copyright notice and this permission notice shall be included in
|
| 9 |
+
all copies or substantial portions of the Software.
|
| 10 |
+
|
| 11 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 12 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 13 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 14 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 15 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
| 16 |
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
| 17 |
+
IN THE SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2010-202x The platformdirs developers
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2006-2022 by the respective authors (see AUTHORS file).
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without
|
| 5 |
+
modification, are permitted provided that the following conditions are
|
| 6 |
+
met:
|
| 7 |
+
|
| 8 |
+
* Redistributions of source code must retain the above copyright
|
| 9 |
+
notice, this list of conditions and the following disclaimer.
|
| 10 |
+
|
| 11 |
+
* Redistributions in binary form must reproduce the above copyright
|
| 12 |
+
notice, this list of conditions and the following disclaimer in the
|
| 13 |
+
documentation and/or other materials provided with the distribution.
|
| 14 |
+
|
| 15 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 16 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 17 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 18 |
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 19 |
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 20 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 21 |
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 22 |
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 23 |
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 24 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 25 |
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2017 Thomas Kluyver
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in
|
| 13 |
+
all copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 21 |
+
THE SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2018, Tzu-ping Chung <uranusjr@gmail.com>
|
| 2 |
+
|
| 3 |
+
Permission to use, copy, modify, and distribute this software for any
|
| 4 |
+
purpose with or without fee is hereby granted, provided that the above
|
| 5 |
+
copyright notice and this permission notice appear in all copies.
|
| 6 |
+
|
| 7 |
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
| 8 |
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
| 9 |
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
| 10 |
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
| 11 |
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
| 12 |
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
| 13 |
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2020 Will McGugan
|
| 2 |
+
|
| 3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 4 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 5 |
+
in the Software without restriction, including without limitation the rights
|
| 6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 7 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 8 |
+
furnished to do so, subject to the following conditions:
|
| 9 |
+
|
| 10 |
+
The above copyright notice and this permission notice shall be included in all
|
| 11 |
+
copies or substantial portions of the Software.
|
| 12 |
+
|
| 13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 19 |
+
SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2021 Taneli Hukkinen
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2021 Taneli Hukkinen
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2022 Seth Michael Larson
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in
|
| 13 |
+
all copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 21 |
+
THE SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2008-2020 Andrey Petrov and contributors.
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/connection.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import typing
|
| 5 |
+
|
| 6 |
+
# use http.client.HTTPException for consistency with non-emscripten
|
| 7 |
+
from http.client import HTTPException as HTTPException # noqa: F401
|
| 8 |
+
from http.client import ResponseNotReady
|
| 9 |
+
|
| 10 |
+
from ..._base_connection import _TYPE_BODY
|
| 11 |
+
from ...connection import HTTPConnection, ProxyConfig, port_by_scheme
|
| 12 |
+
from ...exceptions import TimeoutError
|
| 13 |
+
from ...response import BaseHTTPResponse
|
| 14 |
+
from ...util.connection import _TYPE_SOCKET_OPTIONS
|
| 15 |
+
from ...util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT
|
| 16 |
+
from ...util.url import Url
|
| 17 |
+
from .fetch import _RequestError, _TimeoutError, send_request, send_streaming_request
|
| 18 |
+
from .request import EmscriptenRequest
|
| 19 |
+
from .response import EmscriptenHttpResponseWrapper, EmscriptenResponse
|
| 20 |
+
|
| 21 |
+
if typing.TYPE_CHECKING:
|
| 22 |
+
from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class EmscriptenHTTPConnection:
|
| 26 |
+
default_port: typing.ClassVar[int] = port_by_scheme["http"]
|
| 27 |
+
default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS]
|
| 28 |
+
|
| 29 |
+
timeout: None | (float)
|
| 30 |
+
|
| 31 |
+
host: str
|
| 32 |
+
port: int
|
| 33 |
+
blocksize: int
|
| 34 |
+
source_address: tuple[str, int] | None
|
| 35 |
+
socket_options: _TYPE_SOCKET_OPTIONS | None
|
| 36 |
+
|
| 37 |
+
proxy: Url | None
|
| 38 |
+
proxy_config: ProxyConfig | None
|
| 39 |
+
|
| 40 |
+
is_verified: bool = False
|
| 41 |
+
proxy_is_verified: bool | None = None
|
| 42 |
+
|
| 43 |
+
response_class: type[BaseHTTPResponse] = EmscriptenHttpResponseWrapper
|
| 44 |
+
_response: EmscriptenResponse | None
|
| 45 |
+
|
| 46 |
+
def __init__(
|
| 47 |
+
self,
|
| 48 |
+
host: str,
|
| 49 |
+
port: int = 0,
|
| 50 |
+
*,
|
| 51 |
+
timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,
|
| 52 |
+
source_address: tuple[str, int] | None = None,
|
| 53 |
+
blocksize: int = 8192,
|
| 54 |
+
socket_options: _TYPE_SOCKET_OPTIONS | None = None,
|
| 55 |
+
proxy: Url | None = None,
|
| 56 |
+
proxy_config: ProxyConfig | None = None,
|
| 57 |
+
) -> None:
|
| 58 |
+
self.host = host
|
| 59 |
+
self.port = port
|
| 60 |
+
self.timeout = timeout if isinstance(timeout, float) else 0.0
|
| 61 |
+
self.scheme = "http"
|
| 62 |
+
self._closed = True
|
| 63 |
+
self._response = None
|
| 64 |
+
# ignore these things because we don't
|
| 65 |
+
# have control over that stuff
|
| 66 |
+
self.proxy = None
|
| 67 |
+
self.proxy_config = None
|
| 68 |
+
self.blocksize = blocksize
|
| 69 |
+
self.source_address = None
|
| 70 |
+
self.socket_options = None
|
| 71 |
+
self.is_verified = False
|
| 72 |
+
|
| 73 |
+
def set_tunnel(
|
| 74 |
+
self,
|
| 75 |
+
host: str,
|
| 76 |
+
port: int | None = 0,
|
| 77 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 78 |
+
scheme: str = "http",
|
| 79 |
+
) -> None:
|
| 80 |
+
pass
|
| 81 |
+
|
| 82 |
+
def connect(self) -> None:
|
| 83 |
+
pass
|
| 84 |
+
|
| 85 |
+
def request(
|
| 86 |
+
self,
|
| 87 |
+
method: str,
|
| 88 |
+
url: str,
|
| 89 |
+
body: _TYPE_BODY | None = None,
|
| 90 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 91 |
+
# We know *at least* botocore is depending on the order of the
|
| 92 |
+
# first 3 parameters so to be safe we only mark the later ones
|
| 93 |
+
# as keyword-only to ensure we have space to extend.
|
| 94 |
+
*,
|
| 95 |
+
chunked: bool = False,
|
| 96 |
+
preload_content: bool = True,
|
| 97 |
+
decode_content: bool = True,
|
| 98 |
+
enforce_content_length: bool = True,
|
| 99 |
+
) -> None:
|
| 100 |
+
self._closed = False
|
| 101 |
+
if url.startswith("/"):
|
| 102 |
+
if self.port is not None:
|
| 103 |
+
port = f":{self.port}"
|
| 104 |
+
else:
|
| 105 |
+
port = ""
|
| 106 |
+
# no scheme / host / port included, make a full url
|
| 107 |
+
url = f"{self.scheme}://{self.host}{port}{url}"
|
| 108 |
+
request = EmscriptenRequest(
|
| 109 |
+
url=url,
|
| 110 |
+
method=method,
|
| 111 |
+
timeout=self.timeout if self.timeout else 0,
|
| 112 |
+
decode_content=decode_content,
|
| 113 |
+
)
|
| 114 |
+
request.set_body(body)
|
| 115 |
+
if headers:
|
| 116 |
+
for k, v in headers.items():
|
| 117 |
+
request.set_header(k, v)
|
| 118 |
+
self._response = None
|
| 119 |
+
try:
|
| 120 |
+
if not preload_content:
|
| 121 |
+
self._response = send_streaming_request(request)
|
| 122 |
+
if self._response is None:
|
| 123 |
+
self._response = send_request(request)
|
| 124 |
+
except _TimeoutError as e:
|
| 125 |
+
raise TimeoutError(e.message) from e
|
| 126 |
+
except _RequestError as e:
|
| 127 |
+
raise HTTPException(e.message) from e
|
| 128 |
+
|
| 129 |
+
def getresponse(self) -> BaseHTTPResponse:
|
| 130 |
+
if self._response is not None:
|
| 131 |
+
return EmscriptenHttpResponseWrapper(
|
| 132 |
+
internal_response=self._response,
|
| 133 |
+
url=self._response.request.url,
|
| 134 |
+
connection=self,
|
| 135 |
+
)
|
| 136 |
+
else:
|
| 137 |
+
raise ResponseNotReady()
|
| 138 |
+
|
| 139 |
+
def close(self) -> None:
|
| 140 |
+
self._closed = True
|
| 141 |
+
self._response = None
|
| 142 |
+
|
| 143 |
+
@property
|
| 144 |
+
def is_closed(self) -> bool:
|
| 145 |
+
"""Whether the connection either is brand new or has been previously closed.
|
| 146 |
+
If this property is True then both ``is_connected`` and ``has_connected_to_proxy``
|
| 147 |
+
properties must be False.
|
| 148 |
+
"""
|
| 149 |
+
return self._closed
|
| 150 |
+
|
| 151 |
+
@property
|
| 152 |
+
def is_connected(self) -> bool:
|
| 153 |
+
"""Whether the connection is actively connected to any origin (proxy or target)"""
|
| 154 |
+
return True
|
| 155 |
+
|
| 156 |
+
@property
|
| 157 |
+
def has_connected_to_proxy(self) -> bool:
|
| 158 |
+
"""Whether the connection has successfully connected to its proxy.
|
| 159 |
+
This returns False if no proxy is in use. Used to determine whether
|
| 160 |
+
errors are coming from the proxy layer or from tunnelling to the target origin.
|
| 161 |
+
"""
|
| 162 |
+
return False
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class EmscriptenHTTPSConnection(EmscriptenHTTPConnection):
|
| 166 |
+
default_port = port_by_scheme["https"]
|
| 167 |
+
# all this is basically ignored, as browser handles https
|
| 168 |
+
cert_reqs: int | str | None = None
|
| 169 |
+
ca_certs: str | None = None
|
| 170 |
+
ca_cert_dir: str | None = None
|
| 171 |
+
ca_cert_data: None | str | bytes = None
|
| 172 |
+
cert_file: str | None
|
| 173 |
+
key_file: str | None
|
| 174 |
+
key_password: str | None
|
| 175 |
+
ssl_context: typing.Any | None
|
| 176 |
+
ssl_version: int | str | None = None
|
| 177 |
+
ssl_minimum_version: int | None = None
|
| 178 |
+
ssl_maximum_version: int | None = None
|
| 179 |
+
assert_hostname: None | str | typing.Literal[False]
|
| 180 |
+
assert_fingerprint: str | None = None
|
| 181 |
+
|
| 182 |
+
def __init__(
|
| 183 |
+
self,
|
| 184 |
+
host: str,
|
| 185 |
+
port: int = 0,
|
| 186 |
+
*,
|
| 187 |
+
timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,
|
| 188 |
+
source_address: tuple[str, int] | None = None,
|
| 189 |
+
blocksize: int = 16384,
|
| 190 |
+
socket_options: (
|
| 191 |
+
None | _TYPE_SOCKET_OPTIONS
|
| 192 |
+
) = HTTPConnection.default_socket_options,
|
| 193 |
+
proxy: Url | None = None,
|
| 194 |
+
proxy_config: ProxyConfig | None = None,
|
| 195 |
+
cert_reqs: int | str | None = None,
|
| 196 |
+
assert_hostname: None | str | typing.Literal[False] = None,
|
| 197 |
+
assert_fingerprint: str | None = None,
|
| 198 |
+
server_hostname: str | None = None,
|
| 199 |
+
ssl_context: typing.Any | None = None,
|
| 200 |
+
ca_certs: str | None = None,
|
| 201 |
+
ca_cert_dir: str | None = None,
|
| 202 |
+
ca_cert_data: None | str | bytes = None,
|
| 203 |
+
ssl_minimum_version: int | None = None,
|
| 204 |
+
ssl_maximum_version: int | None = None,
|
| 205 |
+
ssl_version: int | str | None = None, # Deprecated
|
| 206 |
+
cert_file: str | None = None,
|
| 207 |
+
key_file: str | None = None,
|
| 208 |
+
key_password: str | None = None,
|
| 209 |
+
) -> None:
|
| 210 |
+
super().__init__(
|
| 211 |
+
host,
|
| 212 |
+
port=port,
|
| 213 |
+
timeout=timeout,
|
| 214 |
+
source_address=source_address,
|
| 215 |
+
blocksize=blocksize,
|
| 216 |
+
socket_options=socket_options,
|
| 217 |
+
proxy=proxy,
|
| 218 |
+
proxy_config=proxy_config,
|
| 219 |
+
)
|
| 220 |
+
self.scheme = "https"
|
| 221 |
+
|
| 222 |
+
self.key_file = key_file
|
| 223 |
+
self.cert_file = cert_file
|
| 224 |
+
self.key_password = key_password
|
| 225 |
+
self.ssl_context = ssl_context
|
| 226 |
+
self.server_hostname = server_hostname
|
| 227 |
+
self.assert_hostname = assert_hostname
|
| 228 |
+
self.assert_fingerprint = assert_fingerprint
|
| 229 |
+
self.ssl_version = ssl_version
|
| 230 |
+
self.ssl_minimum_version = ssl_minimum_version
|
| 231 |
+
self.ssl_maximum_version = ssl_maximum_version
|
| 232 |
+
self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
|
| 233 |
+
self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
|
| 234 |
+
self.ca_cert_data = ca_cert_data
|
| 235 |
+
|
| 236 |
+
self.cert_reqs = None
|
| 237 |
+
|
| 238 |
+
# The browser will automatically verify all requests.
|
| 239 |
+
# We have no control over that setting.
|
| 240 |
+
self.is_verified = True
|
| 241 |
+
|
| 242 |
+
def set_cert(
|
| 243 |
+
self,
|
| 244 |
+
key_file: str | None = None,
|
| 245 |
+
cert_file: str | None = None,
|
| 246 |
+
cert_reqs: int | str | None = None,
|
| 247 |
+
key_password: str | None = None,
|
| 248 |
+
ca_certs: str | None = None,
|
| 249 |
+
assert_hostname: None | str | typing.Literal[False] = None,
|
| 250 |
+
assert_fingerprint: str | None = None,
|
| 251 |
+
ca_cert_dir: str | None = None,
|
| 252 |
+
ca_cert_data: None | str | bytes = None,
|
| 253 |
+
) -> None:
|
| 254 |
+
pass
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
# verify that this class implements BaseHTTP(s) connection correctly
|
| 258 |
+
if typing.TYPE_CHECKING:
|
| 259 |
+
_supports_http_protocol: BaseHTTPConnection = EmscriptenHTTPConnection("", 0)
|
| 260 |
+
_supports_https_protocol: BaseHTTPSConnection = EmscriptenHTTPSConnection("", 0)
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/emscripten_fetch_worker.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
let Status = {
|
| 2 |
+
SUCCESS_HEADER: -1,
|
| 3 |
+
SUCCESS_EOF: -2,
|
| 4 |
+
ERROR_TIMEOUT: -3,
|
| 5 |
+
ERROR_EXCEPTION: -4,
|
| 6 |
+
};
|
| 7 |
+
|
| 8 |
+
let connections = new Map();
|
| 9 |
+
let nextConnectionID = 1;
|
| 10 |
+
const encoder = new TextEncoder();
|
| 11 |
+
|
| 12 |
+
self.addEventListener("message", async function (event) {
|
| 13 |
+
if (event.data.close) {
|
| 14 |
+
let connectionID = event.data.close;
|
| 15 |
+
connections.delete(connectionID);
|
| 16 |
+
return;
|
| 17 |
+
} else if (event.data.getMore) {
|
| 18 |
+
let connectionID = event.data.getMore;
|
| 19 |
+
let { curOffset, value, reader, intBuffer, byteBuffer } =
|
| 20 |
+
connections.get(connectionID);
|
| 21 |
+
// if we still have some in buffer, then just send it back straight away
|
| 22 |
+
if (!value || curOffset >= value.length) {
|
| 23 |
+
// read another buffer if required
|
| 24 |
+
try {
|
| 25 |
+
let readResponse = await reader.read();
|
| 26 |
+
|
| 27 |
+
if (readResponse.done) {
|
| 28 |
+
// read everything - clear connection and return
|
| 29 |
+
connections.delete(connectionID);
|
| 30 |
+
Atomics.store(intBuffer, 0, Status.SUCCESS_EOF);
|
| 31 |
+
Atomics.notify(intBuffer, 0);
|
| 32 |
+
// finished reading successfully
|
| 33 |
+
// return from event handler
|
| 34 |
+
return;
|
| 35 |
+
}
|
| 36 |
+
curOffset = 0;
|
| 37 |
+
connections.get(connectionID).value = readResponse.value;
|
| 38 |
+
value = readResponse.value;
|
| 39 |
+
} catch (error) {
|
| 40 |
+
console.log("Request exception:", error);
|
| 41 |
+
let errorBytes = encoder.encode(error.message);
|
| 42 |
+
let written = errorBytes.length;
|
| 43 |
+
byteBuffer.set(errorBytes);
|
| 44 |
+
intBuffer[1] = written;
|
| 45 |
+
Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION);
|
| 46 |
+
Atomics.notify(intBuffer, 0);
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
// send as much buffer as we can
|
| 51 |
+
let curLen = value.length - curOffset;
|
| 52 |
+
if (curLen > byteBuffer.length) {
|
| 53 |
+
curLen = byteBuffer.length;
|
| 54 |
+
}
|
| 55 |
+
byteBuffer.set(value.subarray(curOffset, curOffset + curLen), 0);
|
| 56 |
+
|
| 57 |
+
Atomics.store(intBuffer, 0, curLen); // store current length in bytes
|
| 58 |
+
Atomics.notify(intBuffer, 0);
|
| 59 |
+
curOffset += curLen;
|
| 60 |
+
connections.get(connectionID).curOffset = curOffset;
|
| 61 |
+
|
| 62 |
+
return;
|
| 63 |
+
} else {
|
| 64 |
+
// start fetch
|
| 65 |
+
let connectionID = nextConnectionID;
|
| 66 |
+
nextConnectionID += 1;
|
| 67 |
+
const intBuffer = new Int32Array(event.data.buffer);
|
| 68 |
+
const byteBuffer = new Uint8Array(event.data.buffer, 8);
|
| 69 |
+
try {
|
| 70 |
+
const response = await fetch(event.data.url, event.data.fetchParams);
|
| 71 |
+
// return the headers first via textencoder
|
| 72 |
+
var headers = [];
|
| 73 |
+
for (const pair of response.headers.entries()) {
|
| 74 |
+
headers.push([pair[0], pair[1]]);
|
| 75 |
+
}
|
| 76 |
+
let headerObj = {
|
| 77 |
+
headers: headers,
|
| 78 |
+
status: response.status,
|
| 79 |
+
connectionID,
|
| 80 |
+
};
|
| 81 |
+
const headerText = JSON.stringify(headerObj);
|
| 82 |
+
let headerBytes = encoder.encode(headerText);
|
| 83 |
+
let written = headerBytes.length;
|
| 84 |
+
byteBuffer.set(headerBytes);
|
| 85 |
+
intBuffer[1] = written;
|
| 86 |
+
// make a connection
|
| 87 |
+
connections.set(connectionID, {
|
| 88 |
+
reader: response.body.getReader(),
|
| 89 |
+
intBuffer: intBuffer,
|
| 90 |
+
byteBuffer: byteBuffer,
|
| 91 |
+
value: undefined,
|
| 92 |
+
curOffset: 0,
|
| 93 |
+
});
|
| 94 |
+
// set header ready
|
| 95 |
+
Atomics.store(intBuffer, 0, Status.SUCCESS_HEADER);
|
| 96 |
+
Atomics.notify(intBuffer, 0);
|
| 97 |
+
// all fetching after this goes through a new postmessage call with getMore
|
| 98 |
+
// this allows for parallel requests
|
| 99 |
+
} catch (error) {
|
| 100 |
+
console.log("Request exception:", error);
|
| 101 |
+
let errorBytes = encoder.encode(error.message);
|
| 102 |
+
let written = errorBytes.length;
|
| 103 |
+
byteBuffer.set(errorBytes);
|
| 104 |
+
intBuffer[1] = written;
|
| 105 |
+
Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION);
|
| 106 |
+
Atomics.notify(intBuffer, 0);
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
});
|
| 110 |
+
self.postMessage({ inited: true });
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/fetch.py
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Support for streaming http requests in emscripten.
|
| 3 |
+
|
| 4 |
+
A few caveats -
|
| 5 |
+
|
| 6 |
+
If your browser (or Node.js) has WebAssembly JavaScript Promise Integration enabled
|
| 7 |
+
https://github.com/WebAssembly/js-promise-integration/blob/main/proposals/js-promise-integration/Overview.md
|
| 8 |
+
*and* you launch pyodide using `pyodide.runPythonAsync`, this will fetch data using the
|
| 9 |
+
JavaScript asynchronous fetch api (wrapped via `pyodide.ffi.call_sync`). In this case
|
| 10 |
+
timeouts and streaming should just work.
|
| 11 |
+
|
| 12 |
+
Otherwise, it uses a combination of XMLHttpRequest and a web-worker for streaming.
|
| 13 |
+
|
| 14 |
+
This approach has several caveats:
|
| 15 |
+
|
| 16 |
+
Firstly, you can't do streaming http in the main UI thread, because atomics.wait isn't allowed.
|
| 17 |
+
Streaming only works if you're running pyodide in a web worker.
|
| 18 |
+
|
| 19 |
+
Secondly, this uses an extra web worker and SharedArrayBuffer to do the asynchronous fetch
|
| 20 |
+
operation, so it requires that you have crossOriginIsolation enabled, by serving over https
|
| 21 |
+
(or from localhost) with the two headers below set:
|
| 22 |
+
|
| 23 |
+
Cross-Origin-Opener-Policy: same-origin
|
| 24 |
+
Cross-Origin-Embedder-Policy: require-corp
|
| 25 |
+
|
| 26 |
+
You can tell if cross origin isolation is successfully enabled by looking at the global crossOriginIsolated variable in
|
| 27 |
+
JavaScript console. If it isn't, streaming requests will fallback to XMLHttpRequest, i.e. getting the whole
|
| 28 |
+
request into a buffer and then returning it. it shows a warning in the JavaScript console in this case.
|
| 29 |
+
|
| 30 |
+
Finally, the webworker which does the streaming fetch is created on initial import, but will only be started once
|
| 31 |
+
control is returned to javascript. Call `await wait_for_streaming_ready()` to wait for streaming fetch.
|
| 32 |
+
|
| 33 |
+
NB: in this code, there are a lot of JavaScript objects. They are named js_*
|
| 34 |
+
to make it clear what type of object they are.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
from __future__ import annotations
|
| 38 |
+
|
| 39 |
+
import io
|
| 40 |
+
import json
|
| 41 |
+
from email.parser import Parser
|
| 42 |
+
from importlib.resources import files
|
| 43 |
+
from typing import TYPE_CHECKING, Any
|
| 44 |
+
|
| 45 |
+
import js # type: ignore[import-not-found]
|
| 46 |
+
from pyodide.ffi import ( # type: ignore[import-not-found]
|
| 47 |
+
JsArray,
|
| 48 |
+
JsException,
|
| 49 |
+
JsProxy,
|
| 50 |
+
to_js,
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
if TYPE_CHECKING:
|
| 54 |
+
from typing_extensions import Buffer
|
| 55 |
+
|
| 56 |
+
from .request import EmscriptenRequest
|
| 57 |
+
from .response import EmscriptenResponse
|
| 58 |
+
|
| 59 |
+
"""
|
| 60 |
+
There are some headers that trigger unintended CORS preflight requests.
|
| 61 |
+
See also https://github.com/koenvo/pyodide-http/issues/22
|
| 62 |
+
"""
|
| 63 |
+
HEADERS_TO_IGNORE = ("user-agent",)
|
| 64 |
+
|
| 65 |
+
SUCCESS_HEADER = -1
|
| 66 |
+
SUCCESS_EOF = -2
|
| 67 |
+
ERROR_TIMEOUT = -3
|
| 68 |
+
ERROR_EXCEPTION = -4
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class _RequestError(Exception):
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
message: str | None = None,
|
| 75 |
+
*,
|
| 76 |
+
request: EmscriptenRequest | None = None,
|
| 77 |
+
response: EmscriptenResponse | None = None,
|
| 78 |
+
):
|
| 79 |
+
self.request = request
|
| 80 |
+
self.response = response
|
| 81 |
+
self.message = message
|
| 82 |
+
super().__init__(self.message)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class _StreamingError(_RequestError):
|
| 86 |
+
pass
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class _TimeoutError(_RequestError):
|
| 90 |
+
pass
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _obj_from_dict(dict_val: dict[str, Any]) -> JsProxy:
|
| 94 |
+
return to_js(dict_val, dict_converter=js.Object.fromEntries)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class _ReadStream(io.RawIOBase):
|
| 98 |
+
def __init__(
|
| 99 |
+
self,
|
| 100 |
+
int_buffer: JsArray,
|
| 101 |
+
byte_buffer: JsArray,
|
| 102 |
+
timeout: float,
|
| 103 |
+
worker: JsProxy,
|
| 104 |
+
connection_id: int,
|
| 105 |
+
request: EmscriptenRequest,
|
| 106 |
+
):
|
| 107 |
+
self.int_buffer = int_buffer
|
| 108 |
+
self.byte_buffer = byte_buffer
|
| 109 |
+
self.read_pos = 0
|
| 110 |
+
self.read_len = 0
|
| 111 |
+
self.connection_id = connection_id
|
| 112 |
+
self.worker = worker
|
| 113 |
+
self.timeout = int(1000 * timeout) if timeout > 0 else None
|
| 114 |
+
self.is_live = True
|
| 115 |
+
self._is_closed = False
|
| 116 |
+
self.request: EmscriptenRequest | None = request
|
| 117 |
+
|
| 118 |
+
def __del__(self) -> None:
|
| 119 |
+
self.close()
|
| 120 |
+
|
| 121 |
+
# this is compatible with _base_connection
|
| 122 |
+
def is_closed(self) -> bool:
|
| 123 |
+
return self._is_closed
|
| 124 |
+
|
| 125 |
+
# for compatibility with RawIOBase
|
| 126 |
+
@property
|
| 127 |
+
def closed(self) -> bool:
|
| 128 |
+
return self.is_closed()
|
| 129 |
+
|
| 130 |
+
def close(self) -> None:
|
| 131 |
+
if self.is_closed():
|
| 132 |
+
return
|
| 133 |
+
self.read_len = 0
|
| 134 |
+
self.read_pos = 0
|
| 135 |
+
self.int_buffer = None
|
| 136 |
+
self.byte_buffer = None
|
| 137 |
+
self._is_closed = True
|
| 138 |
+
self.request = None
|
| 139 |
+
if self.is_live:
|
| 140 |
+
self.worker.postMessage(_obj_from_dict({"close": self.connection_id}))
|
| 141 |
+
self.is_live = False
|
| 142 |
+
super().close()
|
| 143 |
+
|
| 144 |
+
def readable(self) -> bool:
|
| 145 |
+
return True
|
| 146 |
+
|
| 147 |
+
def writable(self) -> bool:
|
| 148 |
+
return False
|
| 149 |
+
|
| 150 |
+
def seekable(self) -> bool:
|
| 151 |
+
return False
|
| 152 |
+
|
| 153 |
+
def readinto(self, byte_obj: Buffer) -> int:
|
| 154 |
+
if not self.int_buffer:
|
| 155 |
+
raise _StreamingError(
|
| 156 |
+
"No buffer for stream in _ReadStream.readinto",
|
| 157 |
+
request=self.request,
|
| 158 |
+
response=None,
|
| 159 |
+
)
|
| 160 |
+
if self.read_len == 0:
|
| 161 |
+
# wait for the worker to send something
|
| 162 |
+
js.Atomics.store(self.int_buffer, 0, ERROR_TIMEOUT)
|
| 163 |
+
self.worker.postMessage(_obj_from_dict({"getMore": self.connection_id}))
|
| 164 |
+
if (
|
| 165 |
+
js.Atomics.wait(self.int_buffer, 0, ERROR_TIMEOUT, self.timeout)
|
| 166 |
+
== "timed-out"
|
| 167 |
+
):
|
| 168 |
+
raise _TimeoutError
|
| 169 |
+
data_len = self.int_buffer[0]
|
| 170 |
+
if data_len > 0:
|
| 171 |
+
self.read_len = data_len
|
| 172 |
+
self.read_pos = 0
|
| 173 |
+
elif data_len == ERROR_EXCEPTION:
|
| 174 |
+
string_len = self.int_buffer[1]
|
| 175 |
+
# decode the error string
|
| 176 |
+
js_decoder = js.TextDecoder.new()
|
| 177 |
+
json_str = js_decoder.decode(self.byte_buffer.slice(0, string_len))
|
| 178 |
+
raise _StreamingError(
|
| 179 |
+
f"Exception thrown in fetch: {json_str}",
|
| 180 |
+
request=self.request,
|
| 181 |
+
response=None,
|
| 182 |
+
)
|
| 183 |
+
else:
|
| 184 |
+
# EOF, free the buffers and return zero
|
| 185 |
+
# and free the request
|
| 186 |
+
self.is_live = False
|
| 187 |
+
self.close()
|
| 188 |
+
return 0
|
| 189 |
+
# copy from int32array to python bytes
|
| 190 |
+
ret_length = min(self.read_len, len(memoryview(byte_obj)))
|
| 191 |
+
subarray = self.byte_buffer.subarray(
|
| 192 |
+
self.read_pos, self.read_pos + ret_length
|
| 193 |
+
).to_py()
|
| 194 |
+
memoryview(byte_obj)[0:ret_length] = subarray
|
| 195 |
+
self.read_len -= ret_length
|
| 196 |
+
self.read_pos += ret_length
|
| 197 |
+
return ret_length
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class _StreamingFetcher:
|
| 201 |
+
def __init__(self) -> None:
|
| 202 |
+
# make web-worker and data buffer on startup
|
| 203 |
+
self.streaming_ready = False
|
| 204 |
+
streaming_worker_code = (
|
| 205 |
+
files(__package__)
|
| 206 |
+
.joinpath("emscripten_fetch_worker.js")
|
| 207 |
+
.read_text(encoding="utf-8")
|
| 208 |
+
)
|
| 209 |
+
js_data_blob = js.Blob.new(
|
| 210 |
+
to_js([streaming_worker_code], create_pyproxies=False),
|
| 211 |
+
_obj_from_dict({"type": "application/javascript"}),
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
def promise_resolver(js_resolve_fn: JsProxy, js_reject_fn: JsProxy) -> None:
|
| 215 |
+
def onMsg(e: JsProxy) -> None:
|
| 216 |
+
self.streaming_ready = True
|
| 217 |
+
js_resolve_fn(e)
|
| 218 |
+
|
| 219 |
+
def onErr(e: JsProxy) -> None:
|
| 220 |
+
js_reject_fn(e) # Defensive: never happens in ci
|
| 221 |
+
|
| 222 |
+
self.js_worker.onmessage = onMsg
|
| 223 |
+
self.js_worker.onerror = onErr
|
| 224 |
+
|
| 225 |
+
js_data_url = js.URL.createObjectURL(js_data_blob)
|
| 226 |
+
self.js_worker = js.globalThis.Worker.new(js_data_url)
|
| 227 |
+
self.js_worker_ready_promise = js.globalThis.Promise.new(promise_resolver)
|
| 228 |
+
|
| 229 |
+
def send(self, request: EmscriptenRequest) -> EmscriptenResponse:
|
| 230 |
+
headers = {
|
| 231 |
+
k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
body = request.body
|
| 235 |
+
fetch_data = {"headers": headers, "body": to_js(body), "method": request.method}
|
| 236 |
+
# start the request off in the worker
|
| 237 |
+
timeout = int(1000 * request.timeout) if request.timeout > 0 else None
|
| 238 |
+
js_shared_buffer = js.SharedArrayBuffer.new(1048576)
|
| 239 |
+
js_int_buffer = js.Int32Array.new(js_shared_buffer)
|
| 240 |
+
js_byte_buffer = js.Uint8Array.new(js_shared_buffer, 8)
|
| 241 |
+
|
| 242 |
+
js.Atomics.store(js_int_buffer, 0, ERROR_TIMEOUT)
|
| 243 |
+
js.Atomics.notify(js_int_buffer, 0)
|
| 244 |
+
js_absolute_url = js.URL.new(request.url, js.location).href
|
| 245 |
+
self.js_worker.postMessage(
|
| 246 |
+
_obj_from_dict(
|
| 247 |
+
{
|
| 248 |
+
"buffer": js_shared_buffer,
|
| 249 |
+
"url": js_absolute_url,
|
| 250 |
+
"fetchParams": fetch_data,
|
| 251 |
+
}
|
| 252 |
+
)
|
| 253 |
+
)
|
| 254 |
+
# wait for the worker to send something
|
| 255 |
+
js.Atomics.wait(js_int_buffer, 0, ERROR_TIMEOUT, timeout)
|
| 256 |
+
if js_int_buffer[0] == ERROR_TIMEOUT:
|
| 257 |
+
raise _TimeoutError(
|
| 258 |
+
"Timeout connecting to streaming request",
|
| 259 |
+
request=request,
|
| 260 |
+
response=None,
|
| 261 |
+
)
|
| 262 |
+
elif js_int_buffer[0] == SUCCESS_HEADER:
|
| 263 |
+
# got response
|
| 264 |
+
# header length is in second int of intBuffer
|
| 265 |
+
string_len = js_int_buffer[1]
|
| 266 |
+
# decode the rest to a JSON string
|
| 267 |
+
js_decoder = js.TextDecoder.new()
|
| 268 |
+
# this does a copy (the slice) because decode can't work on shared array
|
| 269 |
+
# for some silly reason
|
| 270 |
+
json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len))
|
| 271 |
+
# get it as an object
|
| 272 |
+
response_obj = json.loads(json_str)
|
| 273 |
+
return EmscriptenResponse(
|
| 274 |
+
request=request,
|
| 275 |
+
status_code=response_obj["status"],
|
| 276 |
+
headers=response_obj["headers"],
|
| 277 |
+
body=_ReadStream(
|
| 278 |
+
js_int_buffer,
|
| 279 |
+
js_byte_buffer,
|
| 280 |
+
request.timeout,
|
| 281 |
+
self.js_worker,
|
| 282 |
+
response_obj["connectionID"],
|
| 283 |
+
request,
|
| 284 |
+
),
|
| 285 |
+
)
|
| 286 |
+
elif js_int_buffer[0] == ERROR_EXCEPTION:
|
| 287 |
+
string_len = js_int_buffer[1]
|
| 288 |
+
# decode the error string
|
| 289 |
+
js_decoder = js.TextDecoder.new()
|
| 290 |
+
json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len))
|
| 291 |
+
raise _StreamingError(
|
| 292 |
+
f"Exception thrown in fetch: {json_str}", request=request, response=None
|
| 293 |
+
)
|
| 294 |
+
else:
|
| 295 |
+
raise _StreamingError(
|
| 296 |
+
f"Unknown status from worker in fetch: {js_int_buffer[0]}",
|
| 297 |
+
request=request,
|
| 298 |
+
response=None,
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
class _JSPIReadStream(io.RawIOBase):
|
| 303 |
+
"""
|
| 304 |
+
A read stream that uses pyodide.ffi.run_sync to read from a JavaScript fetch
|
| 305 |
+
response. This requires support for WebAssembly JavaScript Promise Integration
|
| 306 |
+
in the containing browser, and for pyodide to be launched via runPythonAsync.
|
| 307 |
+
|
| 308 |
+
:param js_read_stream:
|
| 309 |
+
The JavaScript stream reader
|
| 310 |
+
|
| 311 |
+
:param timeout:
|
| 312 |
+
Timeout in seconds
|
| 313 |
+
|
| 314 |
+
:param request:
|
| 315 |
+
The request we're handling
|
| 316 |
+
|
| 317 |
+
:param response:
|
| 318 |
+
The response this stream relates to
|
| 319 |
+
|
| 320 |
+
:param js_abort_controller:
|
| 321 |
+
A JavaScript AbortController object, used for timeouts
|
| 322 |
+
"""
|
| 323 |
+
|
| 324 |
+
def __init__(
|
| 325 |
+
self,
|
| 326 |
+
js_read_stream: Any,
|
| 327 |
+
timeout: float,
|
| 328 |
+
request: EmscriptenRequest,
|
| 329 |
+
response: EmscriptenResponse,
|
| 330 |
+
js_abort_controller: Any, # JavaScript AbortController for timeouts
|
| 331 |
+
):
|
| 332 |
+
self.js_read_stream = js_read_stream
|
| 333 |
+
self.timeout = timeout
|
| 334 |
+
self._is_closed = False
|
| 335 |
+
self._is_done = False
|
| 336 |
+
self.request: EmscriptenRequest | None = request
|
| 337 |
+
self.response: EmscriptenResponse | None = response
|
| 338 |
+
self.current_buffer = None
|
| 339 |
+
self.current_buffer_pos = 0
|
| 340 |
+
self.js_abort_controller = js_abort_controller
|
| 341 |
+
|
| 342 |
+
def __del__(self) -> None:
|
| 343 |
+
self.close()
|
| 344 |
+
|
| 345 |
+
# this is compatible with _base_connection
|
| 346 |
+
def is_closed(self) -> bool:
|
| 347 |
+
return self._is_closed
|
| 348 |
+
|
| 349 |
+
# for compatibility with RawIOBase
|
| 350 |
+
@property
|
| 351 |
+
def closed(self) -> bool:
|
| 352 |
+
return self.is_closed()
|
| 353 |
+
|
| 354 |
+
def close(self) -> None:
|
| 355 |
+
if self.is_closed():
|
| 356 |
+
return
|
| 357 |
+
self.read_len = 0
|
| 358 |
+
self.read_pos = 0
|
| 359 |
+
self.js_read_stream.cancel()
|
| 360 |
+
self.js_read_stream = None
|
| 361 |
+
self._is_closed = True
|
| 362 |
+
self._is_done = True
|
| 363 |
+
self.request = None
|
| 364 |
+
self.response = None
|
| 365 |
+
super().close()
|
| 366 |
+
|
| 367 |
+
def readable(self) -> bool:
|
| 368 |
+
return True
|
| 369 |
+
|
| 370 |
+
def writable(self) -> bool:
|
| 371 |
+
return False
|
| 372 |
+
|
| 373 |
+
def seekable(self) -> bool:
|
| 374 |
+
return False
|
| 375 |
+
|
| 376 |
+
def _get_next_buffer(self) -> bool:
|
| 377 |
+
result_js = _run_sync_with_timeout(
|
| 378 |
+
self.js_read_stream.read(),
|
| 379 |
+
self.timeout,
|
| 380 |
+
self.js_abort_controller,
|
| 381 |
+
request=self.request,
|
| 382 |
+
response=self.response,
|
| 383 |
+
)
|
| 384 |
+
if result_js.done:
|
| 385 |
+
self._is_done = True
|
| 386 |
+
return False
|
| 387 |
+
else:
|
| 388 |
+
self.current_buffer = result_js.value.to_py()
|
| 389 |
+
self.current_buffer_pos = 0
|
| 390 |
+
return True
|
| 391 |
+
|
| 392 |
+
def readinto(self, byte_obj: Buffer) -> int:
|
| 393 |
+
if self.current_buffer is None:
|
| 394 |
+
if not self._get_next_buffer() or self.current_buffer is None:
|
| 395 |
+
self.close()
|
| 396 |
+
return 0
|
| 397 |
+
ret_length = min(
|
| 398 |
+
len(byte_obj), len(self.current_buffer) - self.current_buffer_pos
|
| 399 |
+
)
|
| 400 |
+
byte_obj[0:ret_length] = self.current_buffer[
|
| 401 |
+
self.current_buffer_pos : self.current_buffer_pos + ret_length
|
| 402 |
+
]
|
| 403 |
+
self.current_buffer_pos += ret_length
|
| 404 |
+
if self.current_buffer_pos == len(self.current_buffer):
|
| 405 |
+
self.current_buffer = None
|
| 406 |
+
return ret_length
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
# check if we are in a worker or not
|
| 410 |
+
def is_in_browser_main_thread() -> bool:
|
| 411 |
+
return hasattr(js, "window") and hasattr(js, "self") and js.self == js.window
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
def is_cross_origin_isolated() -> bool:
|
| 415 |
+
return hasattr(js, "crossOriginIsolated") and js.crossOriginIsolated
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
def is_in_node() -> bool:
|
| 419 |
+
return (
|
| 420 |
+
hasattr(js, "process")
|
| 421 |
+
and hasattr(js.process, "release")
|
| 422 |
+
and hasattr(js.process.release, "name")
|
| 423 |
+
and js.process.release.name == "node"
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def is_worker_available() -> bool:
|
| 428 |
+
return hasattr(js, "Worker") and hasattr(js, "Blob")
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
_fetcher: _StreamingFetcher | None = None
|
| 432 |
+
|
| 433 |
+
if is_worker_available() and (
|
| 434 |
+
(is_cross_origin_isolated() and not is_in_browser_main_thread())
|
| 435 |
+
and (not is_in_node())
|
| 436 |
+
):
|
| 437 |
+
_fetcher = _StreamingFetcher()
|
| 438 |
+
else:
|
| 439 |
+
_fetcher = None
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
NODE_JSPI_ERROR = (
|
| 443 |
+
"urllib3 only works in Node.js with pyodide.runPythonAsync"
|
| 444 |
+
" and requires the flag --experimental-wasm-stack-switching in "
|
| 445 |
+
" versions of node <24."
|
| 446 |
+
)
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def send_streaming_request(request: EmscriptenRequest) -> EmscriptenResponse | None:
|
| 450 |
+
if has_jspi():
|
| 451 |
+
return send_jspi_request(request, True)
|
| 452 |
+
elif is_in_node():
|
| 453 |
+
raise _RequestError(
|
| 454 |
+
message=NODE_JSPI_ERROR,
|
| 455 |
+
request=request,
|
| 456 |
+
response=None,
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
if _fetcher and streaming_ready():
|
| 460 |
+
return _fetcher.send(request)
|
| 461 |
+
else:
|
| 462 |
+
_show_streaming_warning()
|
| 463 |
+
return None
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
_SHOWN_TIMEOUT_WARNING = False
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
def _show_timeout_warning() -> None:
|
| 470 |
+
global _SHOWN_TIMEOUT_WARNING
|
| 471 |
+
if not _SHOWN_TIMEOUT_WARNING:
|
| 472 |
+
_SHOWN_TIMEOUT_WARNING = True
|
| 473 |
+
message = "Warning: Timeout is not available on main browser thread"
|
| 474 |
+
js.console.warn(message)
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
_SHOWN_STREAMING_WARNING = False
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def _show_streaming_warning() -> None:
|
| 481 |
+
global _SHOWN_STREAMING_WARNING
|
| 482 |
+
if not _SHOWN_STREAMING_WARNING:
|
| 483 |
+
_SHOWN_STREAMING_WARNING = True
|
| 484 |
+
message = "Can't stream HTTP requests because: \n"
|
| 485 |
+
if not is_cross_origin_isolated():
|
| 486 |
+
message += " Page is not cross-origin isolated\n"
|
| 487 |
+
if is_in_browser_main_thread():
|
| 488 |
+
message += " Python is running in main browser thread\n"
|
| 489 |
+
if not is_worker_available():
|
| 490 |
+
message += " Worker or Blob classes are not available in this environment." # Defensive: this is always False in browsers that we test in
|
| 491 |
+
if streaming_ready() is False:
|
| 492 |
+
message += """ Streaming fetch worker isn't ready. If you want to be sure that streaming fetch
|
| 493 |
+
is working, you need to call: 'await urllib3.contrib.emscripten.fetch.wait_for_streaming_ready()`"""
|
| 494 |
+
from js import console
|
| 495 |
+
|
| 496 |
+
console.warn(message)
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
def send_request(request: EmscriptenRequest) -> EmscriptenResponse:
|
| 500 |
+
if has_jspi():
|
| 501 |
+
return send_jspi_request(request, False)
|
| 502 |
+
elif is_in_node():
|
| 503 |
+
raise _RequestError(
|
| 504 |
+
message=NODE_JSPI_ERROR,
|
| 505 |
+
request=request,
|
| 506 |
+
response=None,
|
| 507 |
+
)
|
| 508 |
+
try:
|
| 509 |
+
js_xhr = js.XMLHttpRequest.new()
|
| 510 |
+
|
| 511 |
+
if not is_in_browser_main_thread():
|
| 512 |
+
js_xhr.responseType = "arraybuffer"
|
| 513 |
+
if request.timeout:
|
| 514 |
+
js_xhr.timeout = int(request.timeout * 1000)
|
| 515 |
+
else:
|
| 516 |
+
js_xhr.overrideMimeType("text/plain; charset=ISO-8859-15")
|
| 517 |
+
if request.timeout:
|
| 518 |
+
# timeout isn't available on the main thread - show a warning in console
|
| 519 |
+
# if it is set
|
| 520 |
+
_show_timeout_warning()
|
| 521 |
+
|
| 522 |
+
js_xhr.open(request.method, request.url, False)
|
| 523 |
+
for name, value in request.headers.items():
|
| 524 |
+
if name.lower() not in HEADERS_TO_IGNORE:
|
| 525 |
+
js_xhr.setRequestHeader(name, value)
|
| 526 |
+
|
| 527 |
+
js_xhr.send(to_js(request.body))
|
| 528 |
+
|
| 529 |
+
headers = dict(Parser().parsestr(js_xhr.getAllResponseHeaders()))
|
| 530 |
+
|
| 531 |
+
if not is_in_browser_main_thread():
|
| 532 |
+
body = js_xhr.response.to_py().tobytes()
|
| 533 |
+
else:
|
| 534 |
+
body = js_xhr.response.encode("ISO-8859-15")
|
| 535 |
+
return EmscriptenResponse(
|
| 536 |
+
status_code=js_xhr.status, headers=headers, body=body, request=request
|
| 537 |
+
)
|
| 538 |
+
except JsException as err:
|
| 539 |
+
if err.name == "TimeoutError":
|
| 540 |
+
raise _TimeoutError(err.message, request=request)
|
| 541 |
+
elif err.name == "NetworkError":
|
| 542 |
+
raise _RequestError(err.message, request=request)
|
| 543 |
+
else:
|
| 544 |
+
# general http error
|
| 545 |
+
raise _RequestError(err.message, request=request)
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
def send_jspi_request(
|
| 549 |
+
request: EmscriptenRequest, streaming: bool
|
| 550 |
+
) -> EmscriptenResponse:
|
| 551 |
+
"""
|
| 552 |
+
Send a request using WebAssembly JavaScript Promise Integration
|
| 553 |
+
to wrap the asynchronous JavaScript fetch api (experimental).
|
| 554 |
+
|
| 555 |
+
:param request:
|
| 556 |
+
Request to send
|
| 557 |
+
|
| 558 |
+
:param streaming:
|
| 559 |
+
Whether to stream the response
|
| 560 |
+
|
| 561 |
+
:return: The response object
|
| 562 |
+
:rtype: EmscriptenResponse
|
| 563 |
+
"""
|
| 564 |
+
timeout = request.timeout
|
| 565 |
+
js_abort_controller = js.AbortController.new()
|
| 566 |
+
headers = {k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE}
|
| 567 |
+
req_body = request.body
|
| 568 |
+
fetch_data = {
|
| 569 |
+
"headers": headers,
|
| 570 |
+
"body": to_js(req_body),
|
| 571 |
+
"method": request.method,
|
| 572 |
+
"signal": js_abort_controller.signal,
|
| 573 |
+
}
|
| 574 |
+
# Node.js returns the whole response (unlike opaqueredirect in browsers),
|
| 575 |
+
# so urllib3 can set `redirect: manual` to control redirects itself.
|
| 576 |
+
# https://stackoverflow.com/a/78524615
|
| 577 |
+
if _is_node_js():
|
| 578 |
+
fetch_data["redirect"] = "manual"
|
| 579 |
+
# Call JavaScript fetch (async api, returns a promise)
|
| 580 |
+
fetcher_promise_js = js.fetch(request.url, _obj_from_dict(fetch_data))
|
| 581 |
+
# Now suspend WebAssembly until we resolve that promise
|
| 582 |
+
# or time out.
|
| 583 |
+
response_js = _run_sync_with_timeout(
|
| 584 |
+
fetcher_promise_js,
|
| 585 |
+
timeout,
|
| 586 |
+
js_abort_controller,
|
| 587 |
+
request=request,
|
| 588 |
+
response=None,
|
| 589 |
+
)
|
| 590 |
+
headers = {}
|
| 591 |
+
header_iter = response_js.headers.entries()
|
| 592 |
+
while True:
|
| 593 |
+
iter_value_js = header_iter.next()
|
| 594 |
+
if getattr(iter_value_js, "done", False):
|
| 595 |
+
break
|
| 596 |
+
else:
|
| 597 |
+
headers[str(iter_value_js.value[0])] = str(iter_value_js.value[1])
|
| 598 |
+
status_code = response_js.status
|
| 599 |
+
body: bytes | io.RawIOBase = b""
|
| 600 |
+
|
| 601 |
+
response = EmscriptenResponse(
|
| 602 |
+
status_code=status_code, headers=headers, body=b"", request=request
|
| 603 |
+
)
|
| 604 |
+
if streaming:
|
| 605 |
+
# get via inputstream
|
| 606 |
+
if response_js.body is not None:
|
| 607 |
+
# get a reader from the fetch response
|
| 608 |
+
body_stream_js = response_js.body.getReader()
|
| 609 |
+
body = _JSPIReadStream(
|
| 610 |
+
body_stream_js, timeout, request, response, js_abort_controller
|
| 611 |
+
)
|
| 612 |
+
else:
|
| 613 |
+
# get directly via arraybuffer
|
| 614 |
+
# n.b. this is another async JavaScript call.
|
| 615 |
+
body = _run_sync_with_timeout(
|
| 616 |
+
response_js.arrayBuffer(),
|
| 617 |
+
timeout,
|
| 618 |
+
js_abort_controller,
|
| 619 |
+
request=request,
|
| 620 |
+
response=response,
|
| 621 |
+
).to_py()
|
| 622 |
+
response.body = body
|
| 623 |
+
return response
|
| 624 |
+
|
| 625 |
+
|
| 626 |
+
def _run_sync_with_timeout(
|
| 627 |
+
promise: Any,
|
| 628 |
+
timeout: float,
|
| 629 |
+
js_abort_controller: Any,
|
| 630 |
+
request: EmscriptenRequest | None,
|
| 631 |
+
response: EmscriptenResponse | None,
|
| 632 |
+
) -> Any:
|
| 633 |
+
"""
|
| 634 |
+
Await a JavaScript promise synchronously with a timeout which is implemented
|
| 635 |
+
via the AbortController
|
| 636 |
+
|
| 637 |
+
:param promise:
|
| 638 |
+
Javascript promise to await
|
| 639 |
+
|
| 640 |
+
:param timeout:
|
| 641 |
+
Timeout in seconds
|
| 642 |
+
|
| 643 |
+
:param js_abort_controller:
|
| 644 |
+
A JavaScript AbortController object, used on timeout
|
| 645 |
+
|
| 646 |
+
:param request:
|
| 647 |
+
The request being handled
|
| 648 |
+
|
| 649 |
+
:param response:
|
| 650 |
+
The response being handled (if it exists yet)
|
| 651 |
+
|
| 652 |
+
:raises _TimeoutError: If the request times out
|
| 653 |
+
:raises _RequestError: If the request raises a JavaScript exception
|
| 654 |
+
|
| 655 |
+
:return: The result of awaiting the promise.
|
| 656 |
+
"""
|
| 657 |
+
timer_id = None
|
| 658 |
+
if timeout > 0:
|
| 659 |
+
timer_id = js.setTimeout(
|
| 660 |
+
js_abort_controller.abort.bind(js_abort_controller), int(timeout * 1000)
|
| 661 |
+
)
|
| 662 |
+
try:
|
| 663 |
+
from pyodide.ffi import run_sync
|
| 664 |
+
|
| 665 |
+
# run_sync here uses WebAssembly JavaScript Promise Integration to
|
| 666 |
+
# suspend python until the JavaScript promise resolves.
|
| 667 |
+
return run_sync(promise)
|
| 668 |
+
except JsException as err:
|
| 669 |
+
if err.name == "AbortError":
|
| 670 |
+
raise _TimeoutError(
|
| 671 |
+
message="Request timed out", request=request, response=response
|
| 672 |
+
)
|
| 673 |
+
else:
|
| 674 |
+
raise _RequestError(message=err.message, request=request, response=response)
|
| 675 |
+
finally:
|
| 676 |
+
if timer_id is not None:
|
| 677 |
+
js.clearTimeout(timer_id)
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
def has_jspi() -> bool:
|
| 681 |
+
"""
|
| 682 |
+
Return true if jspi can be used.
|
| 683 |
+
|
| 684 |
+
This requires both browser support and also WebAssembly
|
| 685 |
+
to be in the correct state - i.e. that the javascript
|
| 686 |
+
call into python was async not sync.
|
| 687 |
+
|
| 688 |
+
:return: True if jspi can be used.
|
| 689 |
+
:rtype: bool
|
| 690 |
+
"""
|
| 691 |
+
try:
|
| 692 |
+
from pyodide.ffi import can_run_sync, run_sync # noqa: F401
|
| 693 |
+
|
| 694 |
+
return bool(can_run_sync())
|
| 695 |
+
except ImportError:
|
| 696 |
+
return False
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
def _is_node_js() -> bool:
|
| 700 |
+
"""
|
| 701 |
+
Check if we are in Node.js.
|
| 702 |
+
|
| 703 |
+
:return: True if we are in Node.js.
|
| 704 |
+
:rtype: bool
|
| 705 |
+
"""
|
| 706 |
+
return (
|
| 707 |
+
hasattr(js, "process")
|
| 708 |
+
and hasattr(js.process, "release")
|
| 709 |
+
# According to the Node.js documentation, the release name is always "node".
|
| 710 |
+
and js.process.release.name == "node"
|
| 711 |
+
)
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
def streaming_ready() -> bool | None:
|
| 715 |
+
if _fetcher:
|
| 716 |
+
return _fetcher.streaming_ready
|
| 717 |
+
else:
|
| 718 |
+
return None # no fetcher, return None to signify that
|
| 719 |
+
|
| 720 |
+
|
| 721 |
+
async def wait_for_streaming_ready() -> bool:
|
| 722 |
+
if _fetcher:
|
| 723 |
+
await _fetcher.js_worker_ready_promise
|
| 724 |
+
return True
|
| 725 |
+
else:
|
| 726 |
+
return False
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/request.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass, field
|
| 4 |
+
|
| 5 |
+
from ..._base_connection import _TYPE_BODY
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class EmscriptenRequest:
|
| 10 |
+
method: str
|
| 11 |
+
url: str
|
| 12 |
+
params: dict[str, str] | None = None
|
| 13 |
+
body: _TYPE_BODY | None = None
|
| 14 |
+
headers: dict[str, str] = field(default_factory=dict)
|
| 15 |
+
timeout: float = 0
|
| 16 |
+
decode_content: bool = True
|
| 17 |
+
|
| 18 |
+
def set_header(self, name: str, value: str) -> None:
|
| 19 |
+
self.headers[name.capitalize()] = value
|
| 20 |
+
|
| 21 |
+
def set_body(self, body: _TYPE_BODY | None) -> None:
|
| 22 |
+
self.body = body
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/emscripten/response.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json as _json
|
| 4 |
+
import logging
|
| 5 |
+
import typing
|
| 6 |
+
from contextlib import contextmanager
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
from http.client import HTTPException as HTTPException
|
| 9 |
+
from io import BytesIO, IOBase
|
| 10 |
+
|
| 11 |
+
from ...exceptions import InvalidHeader, TimeoutError
|
| 12 |
+
from ...response import BaseHTTPResponse
|
| 13 |
+
from ...util.retry import Retry
|
| 14 |
+
from .request import EmscriptenRequest
|
| 15 |
+
|
| 16 |
+
if typing.TYPE_CHECKING:
|
| 17 |
+
from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection
|
| 18 |
+
|
| 19 |
+
log = logging.getLogger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class EmscriptenResponse:
|
| 24 |
+
status_code: int
|
| 25 |
+
headers: dict[str, str]
|
| 26 |
+
body: IOBase | bytes
|
| 27 |
+
request: EmscriptenRequest
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class EmscriptenHttpResponseWrapper(BaseHTTPResponse):
|
| 31 |
+
def __init__(
|
| 32 |
+
self,
|
| 33 |
+
internal_response: EmscriptenResponse,
|
| 34 |
+
url: str | None = None,
|
| 35 |
+
connection: BaseHTTPConnection | BaseHTTPSConnection | None = None,
|
| 36 |
+
):
|
| 37 |
+
self._pool = None # set by pool class
|
| 38 |
+
self._body = None
|
| 39 |
+
self._uncached_read_occurred = False
|
| 40 |
+
self._response = internal_response
|
| 41 |
+
self._url = url
|
| 42 |
+
self._connection = connection
|
| 43 |
+
self._closed = False
|
| 44 |
+
super().__init__(
|
| 45 |
+
headers=internal_response.headers,
|
| 46 |
+
status=internal_response.status_code,
|
| 47 |
+
request_url=url,
|
| 48 |
+
version=0,
|
| 49 |
+
version_string="HTTP/?",
|
| 50 |
+
reason="",
|
| 51 |
+
decode_content=True,
|
| 52 |
+
)
|
| 53 |
+
self.length_remaining = self._init_length(self._response.request.method)
|
| 54 |
+
self.length_is_certain = False
|
| 55 |
+
|
| 56 |
+
@property
|
| 57 |
+
def url(self) -> str | None:
|
| 58 |
+
return self._url
|
| 59 |
+
|
| 60 |
+
@url.setter
|
| 61 |
+
def url(self, url: str | None) -> None:
|
| 62 |
+
self._url = url
|
| 63 |
+
|
| 64 |
+
@property
|
| 65 |
+
def connection(self) -> BaseHTTPConnection | BaseHTTPSConnection | None:
|
| 66 |
+
return self._connection
|
| 67 |
+
|
| 68 |
+
@property
|
| 69 |
+
def retries(self) -> Retry | None:
|
| 70 |
+
return self._retries
|
| 71 |
+
|
| 72 |
+
@retries.setter
|
| 73 |
+
def retries(self, retries: Retry | None) -> None:
|
| 74 |
+
# Override the request_url if retries has a redirect location.
|
| 75 |
+
self._retries = retries
|
| 76 |
+
|
| 77 |
+
def stream(
|
| 78 |
+
self, amt: int | None = 2**16, decode_content: bool | None = None
|
| 79 |
+
) -> typing.Generator[bytes]:
|
| 80 |
+
"""
|
| 81 |
+
A generator wrapper for the read() method. A call will block until
|
| 82 |
+
``amt`` bytes have been read from the connection or until the
|
| 83 |
+
connection is closed.
|
| 84 |
+
|
| 85 |
+
:param amt:
|
| 86 |
+
How much of the content to read. The generator will return up to
|
| 87 |
+
much data per iteration, but may return less. This is particularly
|
| 88 |
+
likely when using compressed data. However, the empty string will
|
| 89 |
+
never be returned.
|
| 90 |
+
|
| 91 |
+
:param decode_content:
|
| 92 |
+
If True, will attempt to decode the body based on the
|
| 93 |
+
'content-encoding' header.
|
| 94 |
+
"""
|
| 95 |
+
while True:
|
| 96 |
+
data = self.read(amt=amt, decode_content=decode_content)
|
| 97 |
+
|
| 98 |
+
if data:
|
| 99 |
+
yield data
|
| 100 |
+
else:
|
| 101 |
+
break
|
| 102 |
+
|
| 103 |
+
def _init_length(self, request_method: str | None) -> int | None:
|
| 104 |
+
length: int | None
|
| 105 |
+
content_length: str | None = self.headers.get("content-length")
|
| 106 |
+
|
| 107 |
+
if content_length is not None:
|
| 108 |
+
try:
|
| 109 |
+
# RFC 7230 section 3.3.2 specifies multiple content lengths can
|
| 110 |
+
# be sent in a single Content-Length header
|
| 111 |
+
# (e.g. Content-Length: 42, 42). This line ensures the values
|
| 112 |
+
# are all valid ints and that as long as the `set` length is 1,
|
| 113 |
+
# all values are the same. Otherwise, the header is invalid.
|
| 114 |
+
lengths = {int(val) for val in content_length.split(",")}
|
| 115 |
+
if len(lengths) > 1:
|
| 116 |
+
raise InvalidHeader(
|
| 117 |
+
"Content-Length contained multiple "
|
| 118 |
+
"unmatching values (%s)" % content_length
|
| 119 |
+
)
|
| 120 |
+
length = lengths.pop()
|
| 121 |
+
except ValueError:
|
| 122 |
+
length = None
|
| 123 |
+
else:
|
| 124 |
+
if length < 0:
|
| 125 |
+
length = None
|
| 126 |
+
|
| 127 |
+
else: # if content_length is None
|
| 128 |
+
length = None
|
| 129 |
+
|
| 130 |
+
# Check for responses that shouldn't include a body
|
| 131 |
+
if (
|
| 132 |
+
self.status in (204, 304)
|
| 133 |
+
or 100 <= self.status < 200
|
| 134 |
+
or request_method == "HEAD"
|
| 135 |
+
):
|
| 136 |
+
length = 0
|
| 137 |
+
|
| 138 |
+
return length
|
| 139 |
+
|
| 140 |
+
def read(
|
| 141 |
+
self,
|
| 142 |
+
amt: int | None = None,
|
| 143 |
+
decode_content: bool | None = None, # ignored because browser decodes always
|
| 144 |
+
cache_content: bool = False,
|
| 145 |
+
) -> bytes:
|
| 146 |
+
if (
|
| 147 |
+
self._closed
|
| 148 |
+
or self._response is None
|
| 149 |
+
or (isinstance(self._response.body, IOBase) and self._response.body.closed)
|
| 150 |
+
):
|
| 151 |
+
return b""
|
| 152 |
+
|
| 153 |
+
with self._error_catcher():
|
| 154 |
+
# body has been preloaded as a string by XmlHttpRequest
|
| 155 |
+
if not isinstance(self._response.body, IOBase):
|
| 156 |
+
self.length_remaining = len(self._response.body)
|
| 157 |
+
self.length_is_certain = True
|
| 158 |
+
# wrap body in IOStream
|
| 159 |
+
self._response.body = BytesIO(self._response.body)
|
| 160 |
+
if amt is not None and amt >= 0:
|
| 161 |
+
# don't cache partial content
|
| 162 |
+
cache_content = False
|
| 163 |
+
data = self._response.body.read(amt)
|
| 164 |
+
self._uncached_read_occurred = True
|
| 165 |
+
else: # read all we can (and cache it)
|
| 166 |
+
data = self._response.body.read()
|
| 167 |
+
if cache_content and not self._uncached_read_occurred:
|
| 168 |
+
self._body = data
|
| 169 |
+
else:
|
| 170 |
+
self._uncached_read_occurred = True
|
| 171 |
+
if self.length_remaining is not None:
|
| 172 |
+
self.length_remaining = max(self.length_remaining - len(data), 0)
|
| 173 |
+
if len(data) == 0 or (
|
| 174 |
+
self.length_is_certain and self.length_remaining == 0
|
| 175 |
+
):
|
| 176 |
+
# definitely finished reading, close response stream
|
| 177 |
+
self._response.body.close()
|
| 178 |
+
return typing.cast(bytes, data)
|
| 179 |
+
|
| 180 |
+
def read_chunked(
|
| 181 |
+
self,
|
| 182 |
+
amt: int | None = None,
|
| 183 |
+
decode_content: bool | None = None,
|
| 184 |
+
) -> typing.Generator[bytes]:
|
| 185 |
+
# chunked is handled by browser
|
| 186 |
+
while True:
|
| 187 |
+
bytes = self.read(amt, decode_content)
|
| 188 |
+
if not bytes:
|
| 189 |
+
break
|
| 190 |
+
yield bytes
|
| 191 |
+
|
| 192 |
+
def release_conn(self) -> None:
|
| 193 |
+
if not self._pool or not self._connection:
|
| 194 |
+
return None
|
| 195 |
+
|
| 196 |
+
self._pool._put_conn(self._connection)
|
| 197 |
+
self._connection = None
|
| 198 |
+
|
| 199 |
+
def drain_conn(self) -> None:
|
| 200 |
+
self.close()
|
| 201 |
+
|
| 202 |
+
@property
|
| 203 |
+
def data(self) -> bytes:
|
| 204 |
+
if self._body:
|
| 205 |
+
return self._body
|
| 206 |
+
else:
|
| 207 |
+
return self.read(cache_content=True)
|
| 208 |
+
|
| 209 |
+
def json(self) -> typing.Any:
|
| 210 |
+
"""
|
| 211 |
+
Deserializes the body of the HTTP response as a Python object.
|
| 212 |
+
|
| 213 |
+
The body of the HTTP response must be encoded using UTF-8, as per
|
| 214 |
+
`RFC 8529 Section 8.1 <https://www.rfc-editor.org/rfc/rfc8259#section-8.1>`_.
|
| 215 |
+
|
| 216 |
+
To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to
|
| 217 |
+
your custom decoder instead.
|
| 218 |
+
|
| 219 |
+
If the body of the HTTP response is not decodable to UTF-8, a
|
| 220 |
+
`UnicodeDecodeError` will be raised. If the body of the HTTP response is not a
|
| 221 |
+
valid JSON document, a `json.JSONDecodeError` will be raised.
|
| 222 |
+
|
| 223 |
+
Read more :ref:`here <json_content>`.
|
| 224 |
+
|
| 225 |
+
:returns: The body of the HTTP response as a Python object.
|
| 226 |
+
"""
|
| 227 |
+
data = self.data.decode("utf-8")
|
| 228 |
+
return _json.loads(data)
|
| 229 |
+
|
| 230 |
+
def close(self) -> None:
|
| 231 |
+
if not self._closed:
|
| 232 |
+
if isinstance(self._response.body, IOBase):
|
| 233 |
+
self._response.body.close()
|
| 234 |
+
if self._connection:
|
| 235 |
+
self._connection.close()
|
| 236 |
+
self._connection = None
|
| 237 |
+
self._closed = True
|
| 238 |
+
|
| 239 |
+
@contextmanager
|
| 240 |
+
def _error_catcher(self) -> typing.Generator[None]:
|
| 241 |
+
"""
|
| 242 |
+
Catch Emscripten specific exceptions thrown by fetch.py,
|
| 243 |
+
instead re-raising urllib3 variants, so that low-level exceptions
|
| 244 |
+
are not leaked in the high-level api.
|
| 245 |
+
|
| 246 |
+
On exit, release the connection back to the pool.
|
| 247 |
+
"""
|
| 248 |
+
from .fetch import _RequestError, _TimeoutError # avoid circular import
|
| 249 |
+
|
| 250 |
+
clean_exit = False
|
| 251 |
+
|
| 252 |
+
try:
|
| 253 |
+
yield
|
| 254 |
+
# If no exception is thrown, we should avoid cleaning up
|
| 255 |
+
# unnecessarily.
|
| 256 |
+
clean_exit = True
|
| 257 |
+
except _TimeoutError as e:
|
| 258 |
+
raise TimeoutError(str(e))
|
| 259 |
+
except _RequestError as e:
|
| 260 |
+
raise HTTPException(str(e))
|
| 261 |
+
finally:
|
| 262 |
+
# If we didn't terminate cleanly, we need to throw away our
|
| 263 |
+
# connection.
|
| 264 |
+
if not clean_exit:
|
| 265 |
+
# The response may not be closed but we're not going to use it
|
| 266 |
+
# anymore so close it now
|
| 267 |
+
if (
|
| 268 |
+
isinstance(self._response.body, IOBase)
|
| 269 |
+
and not self._response.body.closed
|
| 270 |
+
):
|
| 271 |
+
self._response.body.close()
|
| 272 |
+
# release the connection back to the pool
|
| 273 |
+
self.release_conn()
|
| 274 |
+
else:
|
| 275 |
+
# If we have read everything from the response stream,
|
| 276 |
+
# return the connection back to the pool.
|
| 277 |
+
if (
|
| 278 |
+
isinstance(self._response.body, IOBase)
|
| 279 |
+
and self._response.body.closed
|
| 280 |
+
):
|
| 281 |
+
self.release_conn()
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Module for using pyOpenSSL as a TLS backend. This module was relevant before
|
| 3 |
+
the standard library ``ssl`` module supported SNI, but now that we've dropped
|
| 4 |
+
support for Python 2.7 all relevant Python versions support SNI so
|
| 5 |
+
**this module is no longer recommended**.
|
| 6 |
+
|
| 7 |
+
This needs the following packages installed:
|
| 8 |
+
|
| 9 |
+
* `pyOpenSSL`_ (tested with 19.0.0)
|
| 10 |
+
* `cryptography`_ (minimum 2.3, from pyopenssl)
|
| 11 |
+
* `idna`_ (minimum 2.1, from cryptography)
|
| 12 |
+
|
| 13 |
+
However, pyOpenSSL depends on cryptography, so while we use all three directly here we
|
| 14 |
+
end up having relatively few packages required.
|
| 15 |
+
|
| 16 |
+
You can install them with the following command:
|
| 17 |
+
|
| 18 |
+
.. code-block:: bash
|
| 19 |
+
|
| 20 |
+
$ python -m pip install pyopenssl cryptography idna
|
| 21 |
+
|
| 22 |
+
To activate certificate checking, call
|
| 23 |
+
:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code
|
| 24 |
+
before you begin making HTTP requests. This can be done in a ``sitecustomize``
|
| 25 |
+
module, or at any other time before your application begins using ``urllib3``,
|
| 26 |
+
like this:
|
| 27 |
+
|
| 28 |
+
.. code-block:: python
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
import pip._vendor.urllib3.contrib.pyopenssl as pyopenssl
|
| 32 |
+
pyopenssl.inject_into_urllib3()
|
| 33 |
+
except ImportError:
|
| 34 |
+
pass
|
| 35 |
+
|
| 36 |
+
.. _pyopenssl: https://www.pyopenssl.org
|
| 37 |
+
.. _cryptography: https://cryptography.io
|
| 38 |
+
.. _idna: https://github.com/kjd/idna
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
from __future__ import annotations
|
| 42 |
+
|
| 43 |
+
import OpenSSL.SSL # type: ignore[import-not-found]
|
| 44 |
+
from cryptography import x509
|
| 45 |
+
|
| 46 |
+
try:
|
| 47 |
+
from cryptography.x509 import UnsupportedExtension # type: ignore[attr-defined]
|
| 48 |
+
except ImportError:
|
| 49 |
+
# UnsupportedExtension is gone in cryptography >= 2.1.0
|
| 50 |
+
class UnsupportedExtension(Exception): # type: ignore[no-redef]
|
| 51 |
+
pass
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
import logging
|
| 55 |
+
import ssl
|
| 56 |
+
import typing
|
| 57 |
+
from io import BytesIO
|
| 58 |
+
from socket import socket as socket_cls
|
| 59 |
+
|
| 60 |
+
from .. import util
|
| 61 |
+
|
| 62 |
+
if typing.TYPE_CHECKING:
|
| 63 |
+
from OpenSSL.crypto import X509 # type: ignore[import-not-found]
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
__all__ = ["inject_into_urllib3", "extract_from_urllib3"]
|
| 67 |
+
|
| 68 |
+
# Map from urllib3 to PyOpenSSL compatible parameter-values.
|
| 69 |
+
_openssl_versions: dict[int, int] = {
|
| 70 |
+
util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined]
|
| 71 |
+
util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined]
|
| 72 |
+
ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"):
|
| 76 |
+
_openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD
|
| 77 |
+
|
| 78 |
+
if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"):
|
| 79 |
+
_openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
_stdlib_to_openssl_verify = {
|
| 83 |
+
ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,
|
| 84 |
+
ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,
|
| 85 |
+
ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER
|
| 86 |
+
+ OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
| 87 |
+
}
|
| 88 |
+
_openssl_to_stdlib_verify = {v: k for k, v in _stdlib_to_openssl_verify.items()}
|
| 89 |
+
|
| 90 |
+
# The SSLvX values are the most likely to be missing in the future
|
| 91 |
+
# but we check them all just to be sure.
|
| 92 |
+
_OP_NO_SSLv2_OR_SSLv3: int = getattr(OpenSSL.SSL, "OP_NO_SSLv2", 0) | getattr(
|
| 93 |
+
OpenSSL.SSL, "OP_NO_SSLv3", 0
|
| 94 |
+
)
|
| 95 |
+
_OP_NO_TLSv1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1", 0)
|
| 96 |
+
_OP_NO_TLSv1_1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_1", 0)
|
| 97 |
+
_OP_NO_TLSv1_2: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_2", 0)
|
| 98 |
+
_OP_NO_TLSv1_3: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_3", 0)
|
| 99 |
+
|
| 100 |
+
_openssl_to_ssl_minimum_version: dict[int, int] = {
|
| 101 |
+
ssl.TLSVersion.MINIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3,
|
| 102 |
+
ssl.TLSVersion.TLSv1: _OP_NO_SSLv2_OR_SSLv3,
|
| 103 |
+
ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1,
|
| 104 |
+
ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1,
|
| 105 |
+
ssl.TLSVersion.TLSv1_3: (
|
| 106 |
+
_OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2
|
| 107 |
+
),
|
| 108 |
+
ssl.TLSVersion.MAXIMUM_SUPPORTED: (
|
| 109 |
+
_OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2
|
| 110 |
+
),
|
| 111 |
+
}
|
| 112 |
+
_openssl_to_ssl_maximum_version: dict[int, int] = {
|
| 113 |
+
ssl.TLSVersion.MINIMUM_SUPPORTED: (
|
| 114 |
+
_OP_NO_SSLv2_OR_SSLv3
|
| 115 |
+
| _OP_NO_TLSv1
|
| 116 |
+
| _OP_NO_TLSv1_1
|
| 117 |
+
| _OP_NO_TLSv1_2
|
| 118 |
+
| _OP_NO_TLSv1_3
|
| 119 |
+
),
|
| 120 |
+
ssl.TLSVersion.TLSv1: (
|
| 121 |
+
_OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3
|
| 122 |
+
),
|
| 123 |
+
ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3,
|
| 124 |
+
ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_3,
|
| 125 |
+
ssl.TLSVersion.TLSv1_3: _OP_NO_SSLv2_OR_SSLv3,
|
| 126 |
+
ssl.TLSVersion.MAXIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3,
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
# OpenSSL will only write 16K at a time
|
| 130 |
+
SSL_WRITE_BLOCKSIZE = 16384
|
| 131 |
+
|
| 132 |
+
orig_util_SSLContext = util.ssl_.SSLContext
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
log = logging.getLogger(__name__)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def inject_into_urllib3() -> None:
|
| 139 |
+
"Monkey-patch urllib3 with PyOpenSSL-backed SSL-support."
|
| 140 |
+
|
| 141 |
+
_validate_dependencies_met()
|
| 142 |
+
|
| 143 |
+
util.SSLContext = PyOpenSSLContext # type: ignore[assignment]
|
| 144 |
+
util.ssl_.SSLContext = PyOpenSSLContext # type: ignore[assignment]
|
| 145 |
+
util.IS_PYOPENSSL = True
|
| 146 |
+
util.ssl_.IS_PYOPENSSL = True
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def extract_from_urllib3() -> None:
|
| 150 |
+
"Undo monkey-patching by :func:`inject_into_urllib3`."
|
| 151 |
+
|
| 152 |
+
util.SSLContext = orig_util_SSLContext
|
| 153 |
+
util.ssl_.SSLContext = orig_util_SSLContext
|
| 154 |
+
util.IS_PYOPENSSL = False
|
| 155 |
+
util.ssl_.IS_PYOPENSSL = False
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def _validate_dependencies_met() -> None:
|
| 159 |
+
"""
|
| 160 |
+
Verifies that PyOpenSSL's package-level dependencies have been met.
|
| 161 |
+
Throws `ImportError` if they are not met.
|
| 162 |
+
"""
|
| 163 |
+
# Method added in `cryptography==1.1`; not available in older versions
|
| 164 |
+
from cryptography.x509.extensions import Extensions
|
| 165 |
+
|
| 166 |
+
if getattr(Extensions, "get_extension_for_class", None) is None:
|
| 167 |
+
raise ImportError(
|
| 168 |
+
"'cryptography' module missing required functionality. "
|
| 169 |
+
"Try upgrading to v1.3.4 or newer."
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
# pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509
|
| 173 |
+
# attribute is only present on those versions.
|
| 174 |
+
from OpenSSL.crypto import X509
|
| 175 |
+
|
| 176 |
+
x509 = X509()
|
| 177 |
+
if getattr(x509, "_x509", None) is None:
|
| 178 |
+
raise ImportError(
|
| 179 |
+
"'pyOpenSSL' module missing required functionality. "
|
| 180 |
+
"Try upgrading to v0.14 or newer."
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def _dnsname_to_stdlib(name: str) -> str | None:
|
| 185 |
+
"""
|
| 186 |
+
Converts a dNSName SubjectAlternativeName field to the form used by the
|
| 187 |
+
standard library on the given Python version.
|
| 188 |
+
|
| 189 |
+
Cryptography produces a dNSName as a unicode string that was idna-decoded
|
| 190 |
+
from ASCII bytes. We need to idna-encode that string to get it back, and
|
| 191 |
+
then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib
|
| 192 |
+
uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8).
|
| 193 |
+
|
| 194 |
+
If the name cannot be idna-encoded then we return None signalling that
|
| 195 |
+
the name given should be skipped.
|
| 196 |
+
"""
|
| 197 |
+
|
| 198 |
+
def idna_encode(name: str) -> bytes | None:
|
| 199 |
+
"""
|
| 200 |
+
Borrowed wholesale from the Python Cryptography Project. It turns out
|
| 201 |
+
that we can't just safely call `idna.encode`: it can explode for
|
| 202 |
+
wildcard names. This avoids that problem.
|
| 203 |
+
"""
|
| 204 |
+
from pip._vendor import idna
|
| 205 |
+
|
| 206 |
+
try:
|
| 207 |
+
for prefix in ["*.", "."]:
|
| 208 |
+
if name.startswith(prefix):
|
| 209 |
+
name = name[len(prefix) :]
|
| 210 |
+
return prefix.encode("ascii") + idna.encode(name)
|
| 211 |
+
return idna.encode(name)
|
| 212 |
+
except idna.core.IDNAError:
|
| 213 |
+
return None
|
| 214 |
+
|
| 215 |
+
# Don't send IPv6 addresses through the IDNA encoder.
|
| 216 |
+
if ":" in name:
|
| 217 |
+
return name
|
| 218 |
+
|
| 219 |
+
encoded_name = idna_encode(name)
|
| 220 |
+
if encoded_name is None:
|
| 221 |
+
return None
|
| 222 |
+
return encoded_name.decode("utf-8")
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def get_subj_alt_name(peer_cert: X509) -> list[tuple[str, str]]:
|
| 226 |
+
"""
|
| 227 |
+
Given an PyOpenSSL certificate, provides all the subject alternative names.
|
| 228 |
+
"""
|
| 229 |
+
cert = peer_cert.to_cryptography()
|
| 230 |
+
|
| 231 |
+
# We want to find the SAN extension. Ask Cryptography to locate it (it's
|
| 232 |
+
# faster than looping in Python)
|
| 233 |
+
try:
|
| 234 |
+
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
|
| 235 |
+
except x509.ExtensionNotFound:
|
| 236 |
+
# No such extension, return the empty list.
|
| 237 |
+
return []
|
| 238 |
+
except (
|
| 239 |
+
x509.DuplicateExtension,
|
| 240 |
+
UnsupportedExtension,
|
| 241 |
+
x509.UnsupportedGeneralNameType,
|
| 242 |
+
UnicodeError,
|
| 243 |
+
) as e:
|
| 244 |
+
# A problem has been found with the quality of the certificate. Assume
|
| 245 |
+
# no SAN field is present.
|
| 246 |
+
log.warning(
|
| 247 |
+
"A problem was encountered with the certificate that prevented "
|
| 248 |
+
"urllib3 from finding the SubjectAlternativeName field. This can "
|
| 249 |
+
"affect certificate validation. The error was %s",
|
| 250 |
+
e,
|
| 251 |
+
)
|
| 252 |
+
return []
|
| 253 |
+
|
| 254 |
+
# We want to return dNSName and iPAddress fields. We need to cast the IPs
|
| 255 |
+
# back to strings because the match_hostname function wants them as
|
| 256 |
+
# strings.
|
| 257 |
+
# Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8
|
| 258 |
+
# decoded. This is pretty frustrating, but that's what the standard library
|
| 259 |
+
# does with certificates, and so we need to attempt to do the same.
|
| 260 |
+
# We also want to skip over names which cannot be idna encoded.
|
| 261 |
+
names = [
|
| 262 |
+
("DNS", name)
|
| 263 |
+
for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName))
|
| 264 |
+
if name is not None
|
| 265 |
+
]
|
| 266 |
+
names.extend(
|
| 267 |
+
("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress)
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
return names
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
class WrappedSocket:
|
| 274 |
+
"""API-compatibility wrapper for Python OpenSSL's Connection-class."""
|
| 275 |
+
|
| 276 |
+
def __init__(
|
| 277 |
+
self,
|
| 278 |
+
connection: OpenSSL.SSL.Connection,
|
| 279 |
+
socket: socket_cls,
|
| 280 |
+
suppress_ragged_eofs: bool = True,
|
| 281 |
+
) -> None:
|
| 282 |
+
self.connection = connection
|
| 283 |
+
self.socket = socket
|
| 284 |
+
self.suppress_ragged_eofs = suppress_ragged_eofs
|
| 285 |
+
self._io_refs = 0
|
| 286 |
+
self._closed = False
|
| 287 |
+
|
| 288 |
+
def fileno(self) -> int:
|
| 289 |
+
return self.socket.fileno()
|
| 290 |
+
|
| 291 |
+
# Copy-pasted from Python 3.5 source code
|
| 292 |
+
def _decref_socketios(self) -> None:
|
| 293 |
+
if self._io_refs > 0:
|
| 294 |
+
self._io_refs -= 1
|
| 295 |
+
if self._closed:
|
| 296 |
+
self.close()
|
| 297 |
+
|
| 298 |
+
def recv(self, *args: typing.Any, **kwargs: typing.Any) -> bytes:
|
| 299 |
+
try:
|
| 300 |
+
data = self.connection.recv(*args, **kwargs)
|
| 301 |
+
except OpenSSL.SSL.SysCallError as e:
|
| 302 |
+
if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"):
|
| 303 |
+
return b""
|
| 304 |
+
else:
|
| 305 |
+
raise OSError(e.args[0], str(e)) from e
|
| 306 |
+
except OpenSSL.SSL.ZeroReturnError:
|
| 307 |
+
if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:
|
| 308 |
+
return b""
|
| 309 |
+
else:
|
| 310 |
+
raise
|
| 311 |
+
except OpenSSL.SSL.WantReadError as e:
|
| 312 |
+
if not util.wait_for_read(self.socket, self.socket.gettimeout()):
|
| 313 |
+
raise TimeoutError("The read operation timed out") from e
|
| 314 |
+
else:
|
| 315 |
+
return self.recv(*args, **kwargs)
|
| 316 |
+
|
| 317 |
+
# TLS 1.3 post-handshake authentication
|
| 318 |
+
except OpenSSL.SSL.Error as e:
|
| 319 |
+
raise ssl.SSLError(f"read error: {e!r}") from e
|
| 320 |
+
else:
|
| 321 |
+
return data # type: ignore[no-any-return]
|
| 322 |
+
|
| 323 |
+
def recv_into(self, *args: typing.Any, **kwargs: typing.Any) -> int:
|
| 324 |
+
try:
|
| 325 |
+
return self.connection.recv_into(*args, **kwargs) # type: ignore[no-any-return]
|
| 326 |
+
except OpenSSL.SSL.SysCallError as e:
|
| 327 |
+
if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"):
|
| 328 |
+
return 0
|
| 329 |
+
else:
|
| 330 |
+
raise OSError(e.args[0], str(e)) from e
|
| 331 |
+
except OpenSSL.SSL.ZeroReturnError:
|
| 332 |
+
if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:
|
| 333 |
+
return 0
|
| 334 |
+
else:
|
| 335 |
+
raise
|
| 336 |
+
except OpenSSL.SSL.WantReadError as e:
|
| 337 |
+
if not util.wait_for_read(self.socket, self.socket.gettimeout()):
|
| 338 |
+
raise TimeoutError("The read operation timed out") from e
|
| 339 |
+
else:
|
| 340 |
+
return self.recv_into(*args, **kwargs)
|
| 341 |
+
|
| 342 |
+
# TLS 1.3 post-handshake authentication
|
| 343 |
+
except OpenSSL.SSL.Error as e:
|
| 344 |
+
raise ssl.SSLError(f"read error: {e!r}") from e
|
| 345 |
+
|
| 346 |
+
def settimeout(self, timeout: float) -> None:
|
| 347 |
+
return self.socket.settimeout(timeout)
|
| 348 |
+
|
| 349 |
+
def _send_until_done(self, data: bytes) -> int:
|
| 350 |
+
while True:
|
| 351 |
+
try:
|
| 352 |
+
return self.connection.send(data) # type: ignore[no-any-return]
|
| 353 |
+
except OpenSSL.SSL.WantWriteError as e:
|
| 354 |
+
if not util.wait_for_write(self.socket, self.socket.gettimeout()):
|
| 355 |
+
raise TimeoutError() from e
|
| 356 |
+
continue
|
| 357 |
+
except OpenSSL.SSL.SysCallError as e:
|
| 358 |
+
raise OSError(e.args[0], str(e)) from e
|
| 359 |
+
|
| 360 |
+
def sendall(self, data: bytes) -> None:
|
| 361 |
+
total_sent = 0
|
| 362 |
+
while total_sent < len(data):
|
| 363 |
+
sent = self._send_until_done(
|
| 364 |
+
data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]
|
| 365 |
+
)
|
| 366 |
+
total_sent += sent
|
| 367 |
+
|
| 368 |
+
def shutdown(self, how: int) -> None:
|
| 369 |
+
try:
|
| 370 |
+
self.connection.shutdown()
|
| 371 |
+
except OpenSSL.SSL.Error as e:
|
| 372 |
+
raise ssl.SSLError(f"shutdown error: {e!r}") from e
|
| 373 |
+
|
| 374 |
+
def close(self) -> None:
|
| 375 |
+
self._closed = True
|
| 376 |
+
if self._io_refs <= 0:
|
| 377 |
+
self._real_close()
|
| 378 |
+
|
| 379 |
+
def _real_close(self) -> None:
|
| 380 |
+
try:
|
| 381 |
+
return self.connection.close() # type: ignore[no-any-return]
|
| 382 |
+
except OpenSSL.SSL.Error:
|
| 383 |
+
return
|
| 384 |
+
|
| 385 |
+
def getpeercert(
|
| 386 |
+
self, binary_form: bool = False
|
| 387 |
+
) -> dict[str, list[typing.Any]] | None:
|
| 388 |
+
x509 = self.connection.get_peer_certificate()
|
| 389 |
+
|
| 390 |
+
if not x509:
|
| 391 |
+
return x509 # type: ignore[no-any-return]
|
| 392 |
+
|
| 393 |
+
if binary_form:
|
| 394 |
+
return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) # type: ignore[no-any-return]
|
| 395 |
+
|
| 396 |
+
return {
|
| 397 |
+
"subject": ((("commonName", x509.get_subject().CN),),), # type: ignore[dict-item]
|
| 398 |
+
"subjectAltName": get_subj_alt_name(x509),
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
def version(self) -> str:
|
| 402 |
+
return self.connection.get_protocol_version_name() # type: ignore[no-any-return]
|
| 403 |
+
|
| 404 |
+
def selected_alpn_protocol(self) -> str | None:
|
| 405 |
+
alpn_proto = self.connection.get_alpn_proto_negotiated()
|
| 406 |
+
return alpn_proto.decode() if alpn_proto else None
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
WrappedSocket.makefile = socket_cls.makefile # type: ignore[attr-defined]
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
class PyOpenSSLContext:
|
| 413 |
+
"""
|
| 414 |
+
I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible
|
| 415 |
+
for translating the interface of the standard library ``SSLContext`` object
|
| 416 |
+
to calls into PyOpenSSL.
|
| 417 |
+
"""
|
| 418 |
+
|
| 419 |
+
def __init__(self, protocol: int) -> None:
|
| 420 |
+
self.protocol = _openssl_versions[protocol]
|
| 421 |
+
self._ctx = OpenSSL.SSL.Context(self.protocol)
|
| 422 |
+
self._options = 0
|
| 423 |
+
self.check_hostname = False
|
| 424 |
+
self._minimum_version: int = ssl.TLSVersion.MINIMUM_SUPPORTED
|
| 425 |
+
self._maximum_version: int = ssl.TLSVersion.MAXIMUM_SUPPORTED
|
| 426 |
+
self._verify_flags: int = ssl.VERIFY_X509_TRUSTED_FIRST
|
| 427 |
+
|
| 428 |
+
@property
|
| 429 |
+
def options(self) -> int:
|
| 430 |
+
return self._options
|
| 431 |
+
|
| 432 |
+
@options.setter
|
| 433 |
+
def options(self, value: int) -> None:
|
| 434 |
+
self._options = value
|
| 435 |
+
self._set_ctx_options()
|
| 436 |
+
|
| 437 |
+
@property
|
| 438 |
+
def verify_flags(self) -> int:
|
| 439 |
+
return self._verify_flags
|
| 440 |
+
|
| 441 |
+
@verify_flags.setter
|
| 442 |
+
def verify_flags(self, value: int) -> None:
|
| 443 |
+
self._verify_flags = value
|
| 444 |
+
self._ctx.get_cert_store().set_flags(self._verify_flags)
|
| 445 |
+
|
| 446 |
+
@property
|
| 447 |
+
def verify_mode(self) -> int:
|
| 448 |
+
return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()]
|
| 449 |
+
|
| 450 |
+
@verify_mode.setter
|
| 451 |
+
def verify_mode(self, value: ssl.VerifyMode) -> None:
|
| 452 |
+
self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback)
|
| 453 |
+
|
| 454 |
+
def set_default_verify_paths(self) -> None:
|
| 455 |
+
self._ctx.set_default_verify_paths()
|
| 456 |
+
|
| 457 |
+
def set_ciphers(self, ciphers: bytes | str) -> None:
|
| 458 |
+
if isinstance(ciphers, str):
|
| 459 |
+
ciphers = ciphers.encode("utf-8")
|
| 460 |
+
self._ctx.set_cipher_list(ciphers)
|
| 461 |
+
|
| 462 |
+
def load_verify_locations(
|
| 463 |
+
self,
|
| 464 |
+
cafile: str | None = None,
|
| 465 |
+
capath: str | None = None,
|
| 466 |
+
cadata: bytes | None = None,
|
| 467 |
+
) -> None:
|
| 468 |
+
if cafile is not None:
|
| 469 |
+
cafile = cafile.encode("utf-8") # type: ignore[assignment]
|
| 470 |
+
if capath is not None:
|
| 471 |
+
capath = capath.encode("utf-8") # type: ignore[assignment]
|
| 472 |
+
try:
|
| 473 |
+
self._ctx.load_verify_locations(cafile, capath)
|
| 474 |
+
if cadata is not None:
|
| 475 |
+
self._ctx.load_verify_locations(BytesIO(cadata))
|
| 476 |
+
except OpenSSL.SSL.Error as e:
|
| 477 |
+
raise ssl.SSLError(f"unable to load trusted certificates: {e!r}") from e
|
| 478 |
+
|
| 479 |
+
def load_cert_chain(
|
| 480 |
+
self,
|
| 481 |
+
certfile: str,
|
| 482 |
+
keyfile: str | None = None,
|
| 483 |
+
password: str | None = None,
|
| 484 |
+
) -> None:
|
| 485 |
+
try:
|
| 486 |
+
self._ctx.use_certificate_chain_file(certfile)
|
| 487 |
+
if password is not None:
|
| 488 |
+
if not isinstance(password, bytes):
|
| 489 |
+
password = password.encode("utf-8") # type: ignore[assignment]
|
| 490 |
+
self._ctx.set_passwd_cb(lambda *_: password)
|
| 491 |
+
self._ctx.use_privatekey_file(keyfile or certfile)
|
| 492 |
+
except OpenSSL.SSL.Error as e:
|
| 493 |
+
raise ssl.SSLError(f"Unable to load certificate chain: {e!r}") from e
|
| 494 |
+
|
| 495 |
+
def set_alpn_protocols(self, protocols: list[bytes | str]) -> None:
|
| 496 |
+
protocols = [util.util.to_bytes(p, "ascii") for p in protocols]
|
| 497 |
+
return self._ctx.set_alpn_protos(protocols) # type: ignore[no-any-return]
|
| 498 |
+
|
| 499 |
+
def wrap_socket(
|
| 500 |
+
self,
|
| 501 |
+
sock: socket_cls,
|
| 502 |
+
server_side: bool = False,
|
| 503 |
+
do_handshake_on_connect: bool = True,
|
| 504 |
+
suppress_ragged_eofs: bool = True,
|
| 505 |
+
server_hostname: bytes | str | None = None,
|
| 506 |
+
) -> WrappedSocket:
|
| 507 |
+
cnx = OpenSSL.SSL.Connection(self._ctx, sock)
|
| 508 |
+
|
| 509 |
+
# If server_hostname is an IP, don't use it for SNI, per RFC6066 Section 3
|
| 510 |
+
if server_hostname and not util.ssl_.is_ipaddress(server_hostname):
|
| 511 |
+
if isinstance(server_hostname, str):
|
| 512 |
+
server_hostname = server_hostname.encode("utf-8")
|
| 513 |
+
cnx.set_tlsext_host_name(server_hostname)
|
| 514 |
+
|
| 515 |
+
cnx.set_connect_state()
|
| 516 |
+
|
| 517 |
+
while True:
|
| 518 |
+
try:
|
| 519 |
+
cnx.do_handshake()
|
| 520 |
+
except OpenSSL.SSL.WantReadError as e:
|
| 521 |
+
if not util.wait_for_read(sock, sock.gettimeout()):
|
| 522 |
+
raise TimeoutError("select timed out") from e
|
| 523 |
+
continue
|
| 524 |
+
except OpenSSL.SSL.Error as e:
|
| 525 |
+
raise ssl.SSLError(f"bad handshake: {e!r}") from e
|
| 526 |
+
break
|
| 527 |
+
|
| 528 |
+
return WrappedSocket(cnx, sock)
|
| 529 |
+
|
| 530 |
+
def _set_ctx_options(self) -> None:
|
| 531 |
+
self._ctx.set_options(
|
| 532 |
+
self._options
|
| 533 |
+
| _openssl_to_ssl_minimum_version[self._minimum_version]
|
| 534 |
+
| _openssl_to_ssl_maximum_version[self._maximum_version]
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
+
@property
|
| 538 |
+
def minimum_version(self) -> int:
|
| 539 |
+
return self._minimum_version
|
| 540 |
+
|
| 541 |
+
@minimum_version.setter
|
| 542 |
+
def minimum_version(self, minimum_version: int) -> None:
|
| 543 |
+
self._minimum_version = minimum_version
|
| 544 |
+
self._set_ctx_options()
|
| 545 |
+
|
| 546 |
+
@property
|
| 547 |
+
def maximum_version(self) -> int:
|
| 548 |
+
return self._maximum_version
|
| 549 |
+
|
| 550 |
+
@maximum_version.setter
|
| 551 |
+
def maximum_version(self, maximum_version: int) -> None:
|
| 552 |
+
self._maximum_version = maximum_version
|
| 553 |
+
self._set_ctx_options()
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
def _verify_callback(
|
| 557 |
+
cnx: OpenSSL.SSL.Connection,
|
| 558 |
+
x509: X509,
|
| 559 |
+
err_no: int,
|
| 560 |
+
err_depth: int,
|
| 561 |
+
return_code: int,
|
| 562 |
+
) -> bool:
|
| 563 |
+
return err_no == 0
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/contrib/socks.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module contains provisional support for SOCKS proxies from within
|
| 3 |
+
urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and
|
| 4 |
+
SOCKS5. To enable its functionality, either install PySocks or install this
|
| 5 |
+
module with the ``socks`` extra.
|
| 6 |
+
|
| 7 |
+
The SOCKS implementation supports the full range of urllib3 features. It also
|
| 8 |
+
supports the following SOCKS features:
|
| 9 |
+
|
| 10 |
+
- SOCKS4A (``proxy_url='socks4a://...``)
|
| 11 |
+
- SOCKS4 (``proxy_url='socks4://...``)
|
| 12 |
+
- SOCKS5 with remote DNS (``proxy_url='socks5h://...``)
|
| 13 |
+
- SOCKS5 with local DNS (``proxy_url='socks5://...``)
|
| 14 |
+
- Usernames and passwords for the SOCKS proxy
|
| 15 |
+
|
| 16 |
+
.. note::
|
| 17 |
+
It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in
|
| 18 |
+
your ``proxy_url`` to ensure that DNS resolution is done from the remote
|
| 19 |
+
server instead of client-side when connecting to a domain name.
|
| 20 |
+
|
| 21 |
+
SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5
|
| 22 |
+
supports IPv4, IPv6, and domain names.
|
| 23 |
+
|
| 24 |
+
When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url``
|
| 25 |
+
will be sent as the ``userid`` section of the SOCKS request:
|
| 26 |
+
|
| 27 |
+
.. code-block:: python
|
| 28 |
+
|
| 29 |
+
proxy_url="socks4a://<userid>@proxy-host"
|
| 30 |
+
|
| 31 |
+
When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion
|
| 32 |
+
of the ``proxy_url`` will be sent as the username/password to authenticate
|
| 33 |
+
with the proxy:
|
| 34 |
+
|
| 35 |
+
.. code-block:: python
|
| 36 |
+
|
| 37 |
+
proxy_url="socks5h://<username>:<password>@proxy-host"
|
| 38 |
+
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
from __future__ import annotations
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
import socks # type: ignore[import-untyped]
|
| 45 |
+
except ImportError:
|
| 46 |
+
import warnings
|
| 47 |
+
|
| 48 |
+
from ..exceptions import DependencyWarning
|
| 49 |
+
|
| 50 |
+
warnings.warn(
|
| 51 |
+
(
|
| 52 |
+
"SOCKS support in urllib3 requires the installation of optional "
|
| 53 |
+
"dependencies: specifically, PySocks. For more information, see "
|
| 54 |
+
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html#socks-proxies"
|
| 55 |
+
),
|
| 56 |
+
DependencyWarning,
|
| 57 |
+
)
|
| 58 |
+
raise
|
| 59 |
+
|
| 60 |
+
import typing
|
| 61 |
+
from socket import timeout as SocketTimeout
|
| 62 |
+
|
| 63 |
+
from ..connection import HTTPConnection, HTTPSConnection
|
| 64 |
+
from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
| 65 |
+
from ..exceptions import ConnectTimeoutError, NewConnectionError
|
| 66 |
+
from ..poolmanager import PoolManager
|
| 67 |
+
from ..util.url import parse_url
|
| 68 |
+
|
| 69 |
+
try:
|
| 70 |
+
import ssl
|
| 71 |
+
except ImportError:
|
| 72 |
+
ssl = None # type: ignore[assignment]
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class _TYPE_SOCKS_OPTIONS(typing.TypedDict):
|
| 76 |
+
socks_version: int
|
| 77 |
+
proxy_host: str | None
|
| 78 |
+
proxy_port: str | None
|
| 79 |
+
username: str | None
|
| 80 |
+
password: str | None
|
| 81 |
+
rdns: bool
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class SOCKSConnection(HTTPConnection):
|
| 85 |
+
"""
|
| 86 |
+
A plain-text HTTP connection that connects via a SOCKS proxy.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
def __init__(
|
| 90 |
+
self,
|
| 91 |
+
_socks_options: _TYPE_SOCKS_OPTIONS,
|
| 92 |
+
*args: typing.Any,
|
| 93 |
+
**kwargs: typing.Any,
|
| 94 |
+
) -> None:
|
| 95 |
+
self._socks_options = _socks_options
|
| 96 |
+
super().__init__(*args, **kwargs)
|
| 97 |
+
|
| 98 |
+
def _new_conn(self) -> socks.socksocket:
|
| 99 |
+
"""
|
| 100 |
+
Establish a new connection via the SOCKS proxy.
|
| 101 |
+
"""
|
| 102 |
+
extra_kw: dict[str, typing.Any] = {}
|
| 103 |
+
if self.source_address:
|
| 104 |
+
extra_kw["source_address"] = self.source_address
|
| 105 |
+
|
| 106 |
+
if self.socket_options:
|
| 107 |
+
extra_kw["socket_options"] = self.socket_options
|
| 108 |
+
|
| 109 |
+
try:
|
| 110 |
+
conn = socks.create_connection(
|
| 111 |
+
(self.host, self.port),
|
| 112 |
+
proxy_type=self._socks_options["socks_version"],
|
| 113 |
+
proxy_addr=self._socks_options["proxy_host"],
|
| 114 |
+
proxy_port=self._socks_options["proxy_port"],
|
| 115 |
+
proxy_username=self._socks_options["username"],
|
| 116 |
+
proxy_password=self._socks_options["password"],
|
| 117 |
+
proxy_rdns=self._socks_options["rdns"],
|
| 118 |
+
timeout=self.timeout,
|
| 119 |
+
**extra_kw,
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
except SocketTimeout as e:
|
| 123 |
+
raise ConnectTimeoutError(
|
| 124 |
+
self,
|
| 125 |
+
f"Connection to {self.host} timed out. (connect timeout={self.timeout})",
|
| 126 |
+
) from e
|
| 127 |
+
|
| 128 |
+
except socks.ProxyError as e:
|
| 129 |
+
# This is fragile as hell, but it seems to be the only way to raise
|
| 130 |
+
# useful errors here.
|
| 131 |
+
if e.socket_err:
|
| 132 |
+
error = e.socket_err
|
| 133 |
+
if isinstance(error, SocketTimeout):
|
| 134 |
+
raise ConnectTimeoutError(
|
| 135 |
+
self,
|
| 136 |
+
f"Connection to {self.host} timed out. (connect timeout={self.timeout})",
|
| 137 |
+
) from e
|
| 138 |
+
else:
|
| 139 |
+
# Adding `from e` messes with coverage somehow, so it's omitted.
|
| 140 |
+
# See #2386.
|
| 141 |
+
raise NewConnectionError(
|
| 142 |
+
self, f"Failed to establish a new connection: {error}"
|
| 143 |
+
)
|
| 144 |
+
else: # Defensive: see https://github.com/urllib3/urllib3/pull/3728#pullrequestreview-3816302703
|
| 145 |
+
raise NewConnectionError(
|
| 146 |
+
self, f"Failed to establish a new connection: {e}"
|
| 147 |
+
) from e
|
| 148 |
+
|
| 149 |
+
except OSError as e: # Defensive: PySocks should catch all these.
|
| 150 |
+
raise NewConnectionError(
|
| 151 |
+
self, f"Failed to establish a new connection: {e}"
|
| 152 |
+
) from e
|
| 153 |
+
|
| 154 |
+
return conn
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
# We don't need to duplicate the Verified/Unverified distinction from
|
| 158 |
+
# urllib3/connection.py here because the HTTPSConnection will already have been
|
| 159 |
+
# correctly set to either the Verified or Unverified form by that module. This
|
| 160 |
+
# means the SOCKSHTTPSConnection will automatically be the correct type.
|
| 161 |
+
class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection):
|
| 162 |
+
pass
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class SOCKSHTTPConnectionPool(HTTPConnectionPool):
|
| 166 |
+
ConnectionCls = SOCKSConnection
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
class SOCKSHTTPSConnectionPool(HTTPSConnectionPool):
|
| 170 |
+
ConnectionCls = SOCKSHTTPSConnection
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class SOCKSProxyManager(PoolManager):
|
| 174 |
+
"""
|
| 175 |
+
A version of the urllib3 ProxyManager that routes connections via the
|
| 176 |
+
defined SOCKS proxy.
|
| 177 |
+
"""
|
| 178 |
+
|
| 179 |
+
pool_classes_by_scheme = {
|
| 180 |
+
"http": SOCKSHTTPConnectionPool,
|
| 181 |
+
"https": SOCKSHTTPSConnectionPool,
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
def __init__(
|
| 185 |
+
self,
|
| 186 |
+
proxy_url: str,
|
| 187 |
+
username: str | None = None,
|
| 188 |
+
password: str | None = None,
|
| 189 |
+
num_pools: int = 10,
|
| 190 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 191 |
+
**connection_pool_kw: typing.Any,
|
| 192 |
+
):
|
| 193 |
+
parsed = parse_url(proxy_url)
|
| 194 |
+
|
| 195 |
+
if username is None and password is None and parsed.auth is not None:
|
| 196 |
+
split = parsed.auth.split(":")
|
| 197 |
+
if len(split) == 2:
|
| 198 |
+
username, password = split
|
| 199 |
+
if parsed.scheme == "socks5":
|
| 200 |
+
socks_version = socks.PROXY_TYPE_SOCKS5
|
| 201 |
+
rdns = False
|
| 202 |
+
elif parsed.scheme == "socks5h":
|
| 203 |
+
socks_version = socks.PROXY_TYPE_SOCKS5
|
| 204 |
+
rdns = True
|
| 205 |
+
elif parsed.scheme == "socks4":
|
| 206 |
+
socks_version = socks.PROXY_TYPE_SOCKS4
|
| 207 |
+
rdns = False
|
| 208 |
+
elif parsed.scheme == "socks4a":
|
| 209 |
+
socks_version = socks.PROXY_TYPE_SOCKS4
|
| 210 |
+
rdns = True
|
| 211 |
+
else:
|
| 212 |
+
raise ValueError(f"Unable to determine SOCKS version from {proxy_url}")
|
| 213 |
+
|
| 214 |
+
self.proxy_url = proxy_url
|
| 215 |
+
|
| 216 |
+
socks_options = {
|
| 217 |
+
"socks_version": socks_version,
|
| 218 |
+
"proxy_host": parsed.host,
|
| 219 |
+
"proxy_port": parsed.port,
|
| 220 |
+
"username": username,
|
| 221 |
+
"password": password,
|
| 222 |
+
"rdns": rdns,
|
| 223 |
+
}
|
| 224 |
+
connection_pool_kw["_socks_options"] = socks_options
|
| 225 |
+
|
| 226 |
+
super().__init__(num_pools, headers, **connection_pool_kw)
|
| 227 |
+
|
| 228 |
+
self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/exceptions.py
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import socket
|
| 4 |
+
import typing
|
| 5 |
+
import warnings
|
| 6 |
+
from email.errors import MessageDefect
|
| 7 |
+
from http.client import IncompleteRead as httplib_IncompleteRead
|
| 8 |
+
|
| 9 |
+
if typing.TYPE_CHECKING:
|
| 10 |
+
from .connection import HTTPConnection
|
| 11 |
+
from .connectionpool import ConnectionPool
|
| 12 |
+
from .response import HTTPResponse
|
| 13 |
+
from .util.retry import Retry
|
| 14 |
+
|
| 15 |
+
# Base Exceptions
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class HTTPError(Exception):
|
| 19 |
+
"""Base exception used by this module."""
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class HTTPWarning(Warning):
|
| 23 |
+
"""Base warning used by this module."""
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
_TYPE_REDUCE_RESULT = tuple[typing.Callable[..., object], tuple[object, ...]]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class PoolError(HTTPError):
|
| 30 |
+
"""Base exception for errors caused within a pool."""
|
| 31 |
+
|
| 32 |
+
def __init__(self, pool: ConnectionPool, message: str) -> None:
|
| 33 |
+
self.pool = pool
|
| 34 |
+
self._message = message
|
| 35 |
+
super().__init__(f"{pool}: {message}")
|
| 36 |
+
|
| 37 |
+
def __reduce__(self) -> _TYPE_REDUCE_RESULT:
|
| 38 |
+
# For pickling purposes.
|
| 39 |
+
return self.__class__, (None, self._message)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class RequestError(PoolError):
|
| 43 |
+
"""Base exception for PoolErrors that have associated URLs."""
|
| 44 |
+
|
| 45 |
+
def __init__(self, pool: ConnectionPool, url: str | None, message: str) -> None:
|
| 46 |
+
self.url = url
|
| 47 |
+
super().__init__(pool, message)
|
| 48 |
+
|
| 49 |
+
def __reduce__(self) -> _TYPE_REDUCE_RESULT:
|
| 50 |
+
# For pickling purposes.
|
| 51 |
+
return self.__class__, (None, self.url, self._message)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class SSLError(HTTPError):
|
| 55 |
+
"""Raised when SSL certificate fails in an HTTPS connection."""
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class ProxyError(HTTPError):
|
| 59 |
+
"""Raised when the connection to a proxy fails."""
|
| 60 |
+
|
| 61 |
+
# The original error is also available as __cause__.
|
| 62 |
+
original_error: Exception
|
| 63 |
+
|
| 64 |
+
def __init__(self, message: str, error: Exception) -> None:
|
| 65 |
+
super().__init__(message, error)
|
| 66 |
+
self.original_error = error
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class DecodeError(HTTPError):
|
| 70 |
+
"""Raised when automatic decoding based on Content-Type fails."""
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class ProtocolError(HTTPError):
|
| 74 |
+
"""Raised when something unexpected happens mid-request/response."""
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
#: Renamed to ProtocolError but aliased for backwards compatibility.
|
| 78 |
+
ConnectionError = ProtocolError
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
# Leaf Exceptions
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class MaxRetryError(RequestError):
|
| 85 |
+
"""Raised when the maximum number of retries is exceeded.
|
| 86 |
+
|
| 87 |
+
:param pool: The connection pool
|
| 88 |
+
:type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool`
|
| 89 |
+
:param str url: The requested Url
|
| 90 |
+
:param reason: The underlying error
|
| 91 |
+
:type reason: :class:`Exception`
|
| 92 |
+
|
| 93 |
+
"""
|
| 94 |
+
|
| 95 |
+
def __init__(
|
| 96 |
+
self, pool: ConnectionPool, url: str | None, reason: Exception | None = None
|
| 97 |
+
) -> None:
|
| 98 |
+
self.reason = reason
|
| 99 |
+
|
| 100 |
+
message = f"Max retries exceeded with url: {url} (Caused by {reason!r})"
|
| 101 |
+
|
| 102 |
+
super().__init__(pool, url, message)
|
| 103 |
+
|
| 104 |
+
def __reduce__(self) -> _TYPE_REDUCE_RESULT:
|
| 105 |
+
# For pickling purposes.
|
| 106 |
+
return self.__class__, (None, self.url, self.reason)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class HostChangedError(RequestError):
|
| 110 |
+
"""Raised when an existing pool gets a request for a foreign host."""
|
| 111 |
+
|
| 112 |
+
def __init__(
|
| 113 |
+
self, pool: ConnectionPool, url: str, retries: Retry | int = 3
|
| 114 |
+
) -> None:
|
| 115 |
+
message = f"Tried to open a foreign host with url: {url}"
|
| 116 |
+
super().__init__(pool, url, message)
|
| 117 |
+
self.retries = retries
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class TimeoutStateError(HTTPError):
|
| 121 |
+
"""Raised when passing an invalid state to a timeout"""
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class TimeoutError(HTTPError):
|
| 125 |
+
"""Raised when a socket timeout error occurs.
|
| 126 |
+
|
| 127 |
+
Catching this error will catch both :exc:`ReadTimeoutErrors
|
| 128 |
+
<ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`.
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
class ReadTimeoutError(TimeoutError, RequestError):
|
| 133 |
+
"""Raised when a socket timeout occurs while receiving data from a server"""
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
# This timeout error does not have a URL attached and needs to inherit from the
|
| 137 |
+
# base HTTPError
|
| 138 |
+
class ConnectTimeoutError(TimeoutError):
|
| 139 |
+
"""Raised when a socket timeout occurs while connecting to a server"""
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class NewConnectionError(ConnectTimeoutError, HTTPError):
|
| 143 |
+
"""Raised when we fail to establish a new connection. Usually ECONNREFUSED."""
|
| 144 |
+
|
| 145 |
+
def __init__(self, conn: HTTPConnection, message: str) -> None:
|
| 146 |
+
self.conn = conn
|
| 147 |
+
self._message = message
|
| 148 |
+
super().__init__(f"{conn}: {message}")
|
| 149 |
+
|
| 150 |
+
def __reduce__(self) -> _TYPE_REDUCE_RESULT:
|
| 151 |
+
# For pickling purposes.
|
| 152 |
+
return self.__class__, (None, self._message)
|
| 153 |
+
|
| 154 |
+
@property
|
| 155 |
+
def pool(self) -> HTTPConnection:
|
| 156 |
+
warnings.warn(
|
| 157 |
+
"The 'pool' property is deprecated and will be removed "
|
| 158 |
+
"in urllib3 v3.0. Use 'conn' instead.",
|
| 159 |
+
FutureWarning,
|
| 160 |
+
stacklevel=2,
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
return self.conn
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class NameResolutionError(NewConnectionError):
|
| 167 |
+
"""Raised when host name resolution fails."""
|
| 168 |
+
|
| 169 |
+
def __init__(self, host: str, conn: HTTPConnection, reason: socket.gaierror):
|
| 170 |
+
message = f"Failed to resolve '{host}' ({reason})"
|
| 171 |
+
self._host = host
|
| 172 |
+
self._reason = reason
|
| 173 |
+
super().__init__(conn, message)
|
| 174 |
+
|
| 175 |
+
def __reduce__(self) -> _TYPE_REDUCE_RESULT:
|
| 176 |
+
# For pickling purposes.
|
| 177 |
+
return self.__class__, (self._host, None, self._reason)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class EmptyPoolError(PoolError):
|
| 181 |
+
"""Raised when a pool runs out of connections and no more are allowed."""
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
class FullPoolError(PoolError):
|
| 185 |
+
"""Raised when we try to add a connection to a full pool in blocking mode."""
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class ClosedPoolError(PoolError):
|
| 189 |
+
"""Raised when a request enters a pool after the pool has been closed."""
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
class LocationValueError(ValueError, HTTPError):
|
| 193 |
+
"""Raised when there is something wrong with a given URL input."""
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
class LocationParseError(LocationValueError):
|
| 197 |
+
"""Raised when get_host or similar fails to parse the URL input."""
|
| 198 |
+
|
| 199 |
+
def __init__(self, location: str) -> None:
|
| 200 |
+
message = f"Failed to parse: {location}"
|
| 201 |
+
super().__init__(message)
|
| 202 |
+
|
| 203 |
+
self.location = location
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class URLSchemeUnknown(LocationValueError):
|
| 207 |
+
"""Raised when a URL input has an unsupported scheme."""
|
| 208 |
+
|
| 209 |
+
def __init__(self, scheme: str):
|
| 210 |
+
message = f"Not supported URL scheme {scheme}"
|
| 211 |
+
super().__init__(message)
|
| 212 |
+
|
| 213 |
+
self.scheme = scheme
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class ResponseError(HTTPError):
|
| 217 |
+
"""Used as a container for an error reason supplied in a MaxRetryError."""
|
| 218 |
+
|
| 219 |
+
GENERIC_ERROR = "too many error responses"
|
| 220 |
+
SPECIFIC_ERROR = "too many {status_code} error responses"
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class SecurityWarning(HTTPWarning):
|
| 224 |
+
"""Warned when performing security reducing actions"""
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
class InsecureRequestWarning(SecurityWarning):
|
| 228 |
+
"""Warned when making an unverified HTTPS request."""
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class NotOpenSSLWarning(SecurityWarning):
|
| 232 |
+
"""Warned when using unsupported SSL library"""
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class SystemTimeWarning(SecurityWarning):
|
| 236 |
+
"""Warned when system time is suspected to be wrong"""
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class InsecurePlatformWarning(SecurityWarning):
|
| 240 |
+
"""Warned when certain TLS/SSL configuration is not available on a platform."""
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class DependencyWarning(HTTPWarning):
|
| 244 |
+
"""
|
| 245 |
+
Warned when an attempt is made to import a module with missing optional
|
| 246 |
+
dependencies.
|
| 247 |
+
"""
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
class ResponseNotChunked(ProtocolError, ValueError):
|
| 251 |
+
"""Response needs to be chunked in order to read it as chunks."""
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
class BodyNotHttplibCompatible(HTTPError):
|
| 255 |
+
"""
|
| 256 |
+
Body should be :class:`http.client.HTTPResponse` like
|
| 257 |
+
(have an fp attribute which returns raw chunks) for read_chunked().
|
| 258 |
+
"""
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
class IncompleteRead(HTTPError, httplib_IncompleteRead):
|
| 262 |
+
"""
|
| 263 |
+
Response length doesn't match expected Content-Length
|
| 264 |
+
|
| 265 |
+
Subclass of :class:`http.client.IncompleteRead` to allow int value
|
| 266 |
+
for ``partial`` to avoid creating large objects on streamed reads.
|
| 267 |
+
"""
|
| 268 |
+
|
| 269 |
+
partial: int # type: ignore[assignment]
|
| 270 |
+
expected: int
|
| 271 |
+
|
| 272 |
+
def __init__(self, partial: int, expected: int) -> None:
|
| 273 |
+
self.partial = partial
|
| 274 |
+
self.expected = expected
|
| 275 |
+
|
| 276 |
+
def __repr__(self) -> str:
|
| 277 |
+
return "IncompleteRead(%i bytes read, %i more expected)" % (
|
| 278 |
+
self.partial,
|
| 279 |
+
self.expected,
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class InvalidChunkLength(HTTPError, httplib_IncompleteRead):
|
| 284 |
+
"""Invalid chunk length in a chunked response."""
|
| 285 |
+
|
| 286 |
+
def __init__(self, response: HTTPResponse, length: bytes) -> None:
|
| 287 |
+
self.partial: int = response.tell() # type: ignore[assignment]
|
| 288 |
+
self.expected: int | None = response.length_remaining
|
| 289 |
+
self.response = response
|
| 290 |
+
self.length = length
|
| 291 |
+
|
| 292 |
+
def __repr__(self) -> str:
|
| 293 |
+
return "InvalidChunkLength(got length %r, %i bytes read)" % (
|
| 294 |
+
self.length,
|
| 295 |
+
self.partial,
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
class InvalidHeader(HTTPError):
|
| 300 |
+
"""The header provided was somehow invalid."""
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
class ProxySchemeUnknown(AssertionError, URLSchemeUnknown):
|
| 304 |
+
"""ProxyManager does not support the supplied scheme"""
|
| 305 |
+
|
| 306 |
+
# TODO(t-8ch): Stop inheriting from AssertionError in v2.0.
|
| 307 |
+
|
| 308 |
+
def __init__(self, scheme: str | None) -> None:
|
| 309 |
+
# 'localhost' is here because our URL parser parses
|
| 310 |
+
# localhost:8080 -> scheme=localhost, remove if we fix this.
|
| 311 |
+
if scheme == "localhost":
|
| 312 |
+
scheme = None
|
| 313 |
+
if scheme is None:
|
| 314 |
+
message = "Proxy URL had no scheme, should start with http:// or https://"
|
| 315 |
+
else:
|
| 316 |
+
message = f"Proxy URL had unsupported scheme {scheme}, should use http:// or https://"
|
| 317 |
+
super().__init__(message)
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
class ProxySchemeUnsupported(ValueError):
|
| 321 |
+
"""Fetching HTTPS resources through HTTPS proxies is unsupported"""
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
class HeaderParsingError(HTTPError):
|
| 325 |
+
"""Raised by assert_header_parsing, but we convert it to a log.warning statement."""
|
| 326 |
+
|
| 327 |
+
def __init__(
|
| 328 |
+
self, defects: list[MessageDefect], unparsed_data: bytes | str | None
|
| 329 |
+
) -> None:
|
| 330 |
+
message = f"{defects or 'Unknown'}, unparsed data: {unparsed_data!r}"
|
| 331 |
+
super().__init__(message)
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
class UnrewindableBodyError(HTTPError):
|
| 335 |
+
"""urllib3 encountered an error when trying to rewind a body"""
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/fields.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import email.utils
|
| 4 |
+
import mimetypes
|
| 5 |
+
import typing
|
| 6 |
+
|
| 7 |
+
_TYPE_FIELD_VALUE = typing.Union[str, bytes]
|
| 8 |
+
_TYPE_FIELD_VALUE_TUPLE = typing.Union[
|
| 9 |
+
_TYPE_FIELD_VALUE,
|
| 10 |
+
tuple[str, _TYPE_FIELD_VALUE],
|
| 11 |
+
tuple[str, _TYPE_FIELD_VALUE, str],
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def guess_content_type(
|
| 16 |
+
filename: str | None, default: str = "application/octet-stream"
|
| 17 |
+
) -> str:
|
| 18 |
+
"""
|
| 19 |
+
Guess the "Content-Type" of a file.
|
| 20 |
+
|
| 21 |
+
:param filename:
|
| 22 |
+
The filename to guess the "Content-Type" of using :mod:`mimetypes`.
|
| 23 |
+
:param default:
|
| 24 |
+
If no "Content-Type" can be guessed, default to `default`.
|
| 25 |
+
"""
|
| 26 |
+
if filename:
|
| 27 |
+
return mimetypes.guess_type(filename)[0] or default
|
| 28 |
+
return default
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def format_header_param_rfc2231(name: str, value: _TYPE_FIELD_VALUE) -> str:
|
| 32 |
+
"""
|
| 33 |
+
Helper function to format and quote a single header parameter using the
|
| 34 |
+
strategy defined in RFC 2231.
|
| 35 |
+
|
| 36 |
+
Particularly useful for header parameters which might contain
|
| 37 |
+
non-ASCII values, like file names. This follows
|
| 38 |
+
`RFC 2388 Section 4.4 <https://tools.ietf.org/html/rfc2388#section-4.4>`_.
|
| 39 |
+
|
| 40 |
+
:param name:
|
| 41 |
+
The name of the parameter, a string expected to be ASCII only.
|
| 42 |
+
:param value:
|
| 43 |
+
The value of the parameter, provided as ``bytes`` or `str``.
|
| 44 |
+
:returns:
|
| 45 |
+
An RFC-2231-formatted unicode string.
|
| 46 |
+
|
| 47 |
+
.. deprecated:: 2.0.0
|
| 48 |
+
Will be removed in urllib3 v3.0. This is not valid for
|
| 49 |
+
``multipart/form-data`` header parameters.
|
| 50 |
+
"""
|
| 51 |
+
import warnings
|
| 52 |
+
|
| 53 |
+
warnings.warn(
|
| 54 |
+
"'format_header_param_rfc2231' is insecure, deprecated and will be "
|
| 55 |
+
"removed in urllib3 v3.0. This is not valid for "
|
| 56 |
+
"multipart/form-data header parameters.",
|
| 57 |
+
FutureWarning,
|
| 58 |
+
stacklevel=2,
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
if isinstance(value, bytes):
|
| 62 |
+
value = value.decode("utf-8")
|
| 63 |
+
|
| 64 |
+
if not any(ch in value for ch in '"\\\r\n'):
|
| 65 |
+
result = f'{name}="{value}"'
|
| 66 |
+
try:
|
| 67 |
+
result.encode("ascii")
|
| 68 |
+
except (UnicodeEncodeError, UnicodeDecodeError):
|
| 69 |
+
pass
|
| 70 |
+
else:
|
| 71 |
+
return result
|
| 72 |
+
|
| 73 |
+
value = email.utils.encode_rfc2231(value, "utf-8")
|
| 74 |
+
value = f"{name}*={value}"
|
| 75 |
+
|
| 76 |
+
return value
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def format_multipart_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str:
|
| 80 |
+
"""
|
| 81 |
+
Format and quote a single multipart header parameter.
|
| 82 |
+
|
| 83 |
+
This follows the `WHATWG HTML Standard`_ as of 2021/06/10, matching
|
| 84 |
+
the behavior of current browser and curl versions. Values are
|
| 85 |
+
assumed to be UTF-8. The ``\\n``, ``\\r``, and ``"`` characters are
|
| 86 |
+
percent encoded.
|
| 87 |
+
|
| 88 |
+
.. _WHATWG HTML Standard:
|
| 89 |
+
https://html.spec.whatwg.org/multipage/
|
| 90 |
+
form-control-infrastructure.html#multipart-form-data
|
| 91 |
+
|
| 92 |
+
:param name:
|
| 93 |
+
The name of the parameter, an ASCII-only ``str``.
|
| 94 |
+
:param value:
|
| 95 |
+
The value of the parameter, a ``str`` or UTF-8 encoded
|
| 96 |
+
``bytes``.
|
| 97 |
+
:returns:
|
| 98 |
+
A string ``name="value"`` with the escaped value.
|
| 99 |
+
|
| 100 |
+
.. versionchanged:: 2.0.0
|
| 101 |
+
Matches the WHATWG HTML Standard as of 2021/06/10. Control
|
| 102 |
+
characters are no longer percent encoded.
|
| 103 |
+
|
| 104 |
+
.. versionchanged:: 2.0.0
|
| 105 |
+
Renamed from ``format_header_param_html5`` and
|
| 106 |
+
``format_header_param``. The old names will be removed in
|
| 107 |
+
urllib3 v3.0.
|
| 108 |
+
"""
|
| 109 |
+
if isinstance(value, bytes):
|
| 110 |
+
value = value.decode("utf-8")
|
| 111 |
+
|
| 112 |
+
# percent encode \n \r "
|
| 113 |
+
value = value.translate({10: "%0A", 13: "%0D", 34: "%22"})
|
| 114 |
+
return f'{name}="{value}"'
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def format_header_param_html5(name: str, value: _TYPE_FIELD_VALUE) -> str:
|
| 118 |
+
"""
|
| 119 |
+
.. deprecated:: 2.0.0
|
| 120 |
+
Renamed to :func:`format_multipart_header_param`. Will be
|
| 121 |
+
removed in urllib3 v3.0.
|
| 122 |
+
"""
|
| 123 |
+
import warnings
|
| 124 |
+
|
| 125 |
+
warnings.warn(
|
| 126 |
+
"'format_header_param_html5' has been renamed to "
|
| 127 |
+
"'format_multipart_header_param'. The old name will be "
|
| 128 |
+
"removed in urllib3 v3.0.",
|
| 129 |
+
FutureWarning,
|
| 130 |
+
stacklevel=2,
|
| 131 |
+
)
|
| 132 |
+
return format_multipart_header_param(name, value)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def format_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str:
|
| 136 |
+
"""
|
| 137 |
+
.. deprecated:: 2.0.0
|
| 138 |
+
Renamed to :func:`format_multipart_header_param`. Will be
|
| 139 |
+
removed in urllib3 v3.0.
|
| 140 |
+
"""
|
| 141 |
+
import warnings
|
| 142 |
+
|
| 143 |
+
warnings.warn(
|
| 144 |
+
"'format_header_param' has been renamed to "
|
| 145 |
+
"'format_multipart_header_param'. The old name will be "
|
| 146 |
+
"removed in urllib3 v3.0.",
|
| 147 |
+
FutureWarning,
|
| 148 |
+
stacklevel=2,
|
| 149 |
+
)
|
| 150 |
+
return format_multipart_header_param(name, value)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class RequestField:
|
| 154 |
+
"""
|
| 155 |
+
A data container for request body parameters.
|
| 156 |
+
|
| 157 |
+
:param name:
|
| 158 |
+
The name of this request field. Must be unicode.
|
| 159 |
+
:param data:
|
| 160 |
+
The data/value body.
|
| 161 |
+
:param filename:
|
| 162 |
+
An optional filename of the request field. Must be unicode.
|
| 163 |
+
:param headers:
|
| 164 |
+
An optional dict-like object of headers to initially use for the field.
|
| 165 |
+
|
| 166 |
+
.. versionchanged:: 2.0.0
|
| 167 |
+
The ``header_formatter`` parameter is deprecated and will
|
| 168 |
+
be removed in urllib3 v3.0.
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
def __init__(
|
| 172 |
+
self,
|
| 173 |
+
name: str,
|
| 174 |
+
data: _TYPE_FIELD_VALUE,
|
| 175 |
+
filename: str | None = None,
|
| 176 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 177 |
+
header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None,
|
| 178 |
+
):
|
| 179 |
+
self._name = name
|
| 180 |
+
self._filename = filename
|
| 181 |
+
self.data = data
|
| 182 |
+
self.headers: dict[str, str | None] = {}
|
| 183 |
+
if headers:
|
| 184 |
+
self.headers = dict(headers)
|
| 185 |
+
|
| 186 |
+
if header_formatter is not None:
|
| 187 |
+
import warnings
|
| 188 |
+
|
| 189 |
+
warnings.warn(
|
| 190 |
+
"The 'header_formatter' parameter is deprecated and "
|
| 191 |
+
"will be removed in urllib3 v3.0.",
|
| 192 |
+
FutureWarning,
|
| 193 |
+
stacklevel=2,
|
| 194 |
+
)
|
| 195 |
+
self.header_formatter = header_formatter
|
| 196 |
+
else:
|
| 197 |
+
self.header_formatter = format_multipart_header_param
|
| 198 |
+
|
| 199 |
+
@classmethod
|
| 200 |
+
def from_tuples(
|
| 201 |
+
cls,
|
| 202 |
+
fieldname: str,
|
| 203 |
+
value: _TYPE_FIELD_VALUE_TUPLE,
|
| 204 |
+
header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None,
|
| 205 |
+
) -> RequestField:
|
| 206 |
+
"""
|
| 207 |
+
A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters.
|
| 208 |
+
|
| 209 |
+
Supports constructing :class:`~urllib3.fields.RequestField` from
|
| 210 |
+
parameter of key/value strings AND key/filetuple. A filetuple is a
|
| 211 |
+
(filename, data, MIME type) tuple where the MIME type is optional.
|
| 212 |
+
For example::
|
| 213 |
+
|
| 214 |
+
'foo': 'bar',
|
| 215 |
+
'fakefile': ('foofile.txt', 'contents of foofile'),
|
| 216 |
+
'realfile': ('barfile.txt', open('realfile').read()),
|
| 217 |
+
'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'),
|
| 218 |
+
'nonamefile': 'contents of nonamefile field',
|
| 219 |
+
|
| 220 |
+
Field names and filenames must be unicode.
|
| 221 |
+
"""
|
| 222 |
+
filename: str | None
|
| 223 |
+
content_type: str | None
|
| 224 |
+
data: _TYPE_FIELD_VALUE
|
| 225 |
+
|
| 226 |
+
if isinstance(value, tuple):
|
| 227 |
+
if len(value) == 3:
|
| 228 |
+
filename, data, content_type = value
|
| 229 |
+
else:
|
| 230 |
+
filename, data = value
|
| 231 |
+
content_type = guess_content_type(filename)
|
| 232 |
+
else:
|
| 233 |
+
filename = None
|
| 234 |
+
content_type = None
|
| 235 |
+
data = value
|
| 236 |
+
|
| 237 |
+
request_param = cls(
|
| 238 |
+
fieldname, data, filename=filename, header_formatter=header_formatter
|
| 239 |
+
)
|
| 240 |
+
request_param.make_multipart(content_type=content_type)
|
| 241 |
+
|
| 242 |
+
return request_param
|
| 243 |
+
|
| 244 |
+
def _render_part(self, name: str, value: _TYPE_FIELD_VALUE) -> str:
|
| 245 |
+
"""
|
| 246 |
+
Override this method to change how each multipart header
|
| 247 |
+
parameter is formatted. By default, this calls
|
| 248 |
+
:func:`format_multipart_header_param`.
|
| 249 |
+
|
| 250 |
+
:param name:
|
| 251 |
+
The name of the parameter, an ASCII-only ``str``.
|
| 252 |
+
:param value:
|
| 253 |
+
The value of the parameter, a ``str`` or UTF-8 encoded
|
| 254 |
+
``bytes``.
|
| 255 |
+
|
| 256 |
+
:meta public:
|
| 257 |
+
"""
|
| 258 |
+
return self.header_formatter(name, value)
|
| 259 |
+
|
| 260 |
+
def _render_parts(
|
| 261 |
+
self,
|
| 262 |
+
header_parts: (
|
| 263 |
+
dict[str, _TYPE_FIELD_VALUE | None]
|
| 264 |
+
| typing.Sequence[tuple[str, _TYPE_FIELD_VALUE | None]]
|
| 265 |
+
),
|
| 266 |
+
) -> str:
|
| 267 |
+
"""
|
| 268 |
+
Helper function to format and quote a single header.
|
| 269 |
+
|
| 270 |
+
Useful for single headers that are composed of multiple items. E.g.,
|
| 271 |
+
'Content-Disposition' fields.
|
| 272 |
+
|
| 273 |
+
:param header_parts:
|
| 274 |
+
A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format
|
| 275 |
+
as `k1="v1"; k2="v2"; ...`.
|
| 276 |
+
"""
|
| 277 |
+
iterable: typing.Iterable[tuple[str, _TYPE_FIELD_VALUE | None]]
|
| 278 |
+
|
| 279 |
+
parts = []
|
| 280 |
+
if isinstance(header_parts, dict):
|
| 281 |
+
iterable = header_parts.items()
|
| 282 |
+
else:
|
| 283 |
+
iterable = header_parts
|
| 284 |
+
|
| 285 |
+
for name, value in iterable:
|
| 286 |
+
if value is not None:
|
| 287 |
+
parts.append(self._render_part(name, value))
|
| 288 |
+
|
| 289 |
+
return "; ".join(parts)
|
| 290 |
+
|
| 291 |
+
def render_headers(self) -> str:
|
| 292 |
+
"""
|
| 293 |
+
Renders the headers for this request field.
|
| 294 |
+
"""
|
| 295 |
+
lines = []
|
| 296 |
+
|
| 297 |
+
sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"]
|
| 298 |
+
for sort_key in sort_keys:
|
| 299 |
+
if self.headers.get(sort_key, False):
|
| 300 |
+
lines.append(f"{sort_key}: {self.headers[sort_key]}")
|
| 301 |
+
|
| 302 |
+
for header_name, header_value in self.headers.items():
|
| 303 |
+
if header_name not in sort_keys:
|
| 304 |
+
if header_value:
|
| 305 |
+
lines.append(f"{header_name}: {header_value}")
|
| 306 |
+
|
| 307 |
+
lines.append("\r\n")
|
| 308 |
+
return "\r\n".join(lines)
|
| 309 |
+
|
| 310 |
+
def make_multipart(
|
| 311 |
+
self,
|
| 312 |
+
content_disposition: str | None = None,
|
| 313 |
+
content_type: str | None = None,
|
| 314 |
+
content_location: str | None = None,
|
| 315 |
+
) -> None:
|
| 316 |
+
"""
|
| 317 |
+
Makes this request field into a multipart request field.
|
| 318 |
+
|
| 319 |
+
This method overrides "Content-Disposition", "Content-Type" and
|
| 320 |
+
"Content-Location" headers to the request parameter.
|
| 321 |
+
|
| 322 |
+
:param content_disposition:
|
| 323 |
+
The 'Content-Disposition' of the request body. Defaults to 'form-data'
|
| 324 |
+
:param content_type:
|
| 325 |
+
The 'Content-Type' of the request body.
|
| 326 |
+
:param content_location:
|
| 327 |
+
The 'Content-Location' of the request body.
|
| 328 |
+
|
| 329 |
+
"""
|
| 330 |
+
content_disposition = (content_disposition or "form-data") + "; ".join(
|
| 331 |
+
[
|
| 332 |
+
"",
|
| 333 |
+
self._render_parts(
|
| 334 |
+
(("name", self._name), ("filename", self._filename))
|
| 335 |
+
),
|
| 336 |
+
]
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
self.headers["Content-Disposition"] = content_disposition
|
| 340 |
+
self.headers["Content-Type"] = content_type
|
| 341 |
+
self.headers["Content-Location"] = content_location
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/filepost.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import binascii
|
| 4 |
+
import codecs
|
| 5 |
+
import os
|
| 6 |
+
import typing
|
| 7 |
+
from io import BytesIO
|
| 8 |
+
|
| 9 |
+
from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField
|
| 10 |
+
|
| 11 |
+
writer = codecs.lookup("utf-8")[3]
|
| 12 |
+
|
| 13 |
+
_TYPE_FIELDS_SEQUENCE = typing.Sequence[
|
| 14 |
+
typing.Union[tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField]
|
| 15 |
+
]
|
| 16 |
+
_TYPE_FIELDS = typing.Union[
|
| 17 |
+
_TYPE_FIELDS_SEQUENCE,
|
| 18 |
+
typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE],
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def choose_boundary() -> str:
|
| 23 |
+
"""
|
| 24 |
+
Our embarrassingly-simple replacement for mimetools.choose_boundary.
|
| 25 |
+
"""
|
| 26 |
+
return binascii.hexlify(os.urandom(16)).decode()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]:
|
| 30 |
+
"""
|
| 31 |
+
Iterate over fields.
|
| 32 |
+
|
| 33 |
+
Supports list of (k, v) tuples and dicts, and lists of
|
| 34 |
+
:class:`~urllib3.fields.RequestField`.
|
| 35 |
+
|
| 36 |
+
"""
|
| 37 |
+
iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]]
|
| 38 |
+
|
| 39 |
+
if isinstance(fields, typing.Mapping):
|
| 40 |
+
iterable = fields.items()
|
| 41 |
+
else:
|
| 42 |
+
iterable = fields
|
| 43 |
+
|
| 44 |
+
for field in iterable:
|
| 45 |
+
if isinstance(field, RequestField):
|
| 46 |
+
yield field
|
| 47 |
+
else:
|
| 48 |
+
yield RequestField.from_tuples(*field)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def encode_multipart_formdata(
|
| 52 |
+
fields: _TYPE_FIELDS, boundary: str | None = None
|
| 53 |
+
) -> tuple[bytes, str]:
|
| 54 |
+
"""
|
| 55 |
+
Encode a dictionary of ``fields`` using the multipart/form-data MIME format.
|
| 56 |
+
|
| 57 |
+
:param fields:
|
| 58 |
+
Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`).
|
| 59 |
+
Values are processed by :func:`urllib3.fields.RequestField.from_tuples`.
|
| 60 |
+
|
| 61 |
+
:param boundary:
|
| 62 |
+
If not specified, then a random boundary will be generated using
|
| 63 |
+
:func:`urllib3.filepost.choose_boundary`.
|
| 64 |
+
"""
|
| 65 |
+
body = BytesIO()
|
| 66 |
+
if boundary is None:
|
| 67 |
+
boundary = choose_boundary()
|
| 68 |
+
|
| 69 |
+
for field in iter_field_objects(fields):
|
| 70 |
+
body.write(f"--{boundary}\r\n".encode("latin-1"))
|
| 71 |
+
|
| 72 |
+
writer(body).write(field.render_headers())
|
| 73 |
+
data = field.data
|
| 74 |
+
|
| 75 |
+
if isinstance(data, int):
|
| 76 |
+
data = str(data) # Backwards compatibility
|
| 77 |
+
|
| 78 |
+
if isinstance(data, str):
|
| 79 |
+
writer(body).write(data)
|
| 80 |
+
else:
|
| 81 |
+
body.write(data)
|
| 82 |
+
|
| 83 |
+
body.write(b"\r\n")
|
| 84 |
+
|
| 85 |
+
body.write(f"--{boundary}--\r\n".encode("latin-1"))
|
| 86 |
+
|
| 87 |
+
content_type = f"multipart/form-data; boundary={boundary}"
|
| 88 |
+
|
| 89 |
+
return body.getvalue(), content_type
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from importlib.metadata import version
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
"inject_into_urllib3",
|
| 7 |
+
"extract_from_urllib3",
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import typing
|
| 11 |
+
|
| 12 |
+
orig_HTTPSConnection: typing.Any = None
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def inject_into_urllib3() -> None:
|
| 16 |
+
# First check if h2 version is valid
|
| 17 |
+
h2_version = version("h2")
|
| 18 |
+
if not h2_version.startswith("4."):
|
| 19 |
+
raise ImportError(
|
| 20 |
+
"urllib3 v2 supports h2 version 4.x.x, currently "
|
| 21 |
+
f"the 'h2' module is compiled with {h2_version!r}. "
|
| 22 |
+
"See: https://github.com/urllib3/urllib3/issues/3290"
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
# Import here to avoid circular dependencies.
|
| 26 |
+
from .. import connection as urllib3_connection
|
| 27 |
+
from .. import util as urllib3_util
|
| 28 |
+
from ..connectionpool import HTTPSConnectionPool
|
| 29 |
+
from ..util import ssl_ as urllib3_util_ssl
|
| 30 |
+
from .connection import HTTP2Connection
|
| 31 |
+
|
| 32 |
+
global orig_HTTPSConnection
|
| 33 |
+
orig_HTTPSConnection = urllib3_connection.HTTPSConnection
|
| 34 |
+
|
| 35 |
+
HTTPSConnectionPool.ConnectionCls = HTTP2Connection
|
| 36 |
+
urllib3_connection.HTTPSConnection = HTTP2Connection # type: ignore[misc]
|
| 37 |
+
|
| 38 |
+
# TODO: Offer 'http/1.1' as well, but for testing purposes this is handy.
|
| 39 |
+
urllib3_util.ALPN_PROTOCOLS = ["h2"]
|
| 40 |
+
urllib3_util_ssl.ALPN_PROTOCOLS = ["h2"]
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def extract_from_urllib3() -> None:
|
| 44 |
+
from .. import connection as urllib3_connection
|
| 45 |
+
from .. import util as urllib3_util
|
| 46 |
+
from ..connectionpool import HTTPSConnectionPool
|
| 47 |
+
from ..util import ssl_ as urllib3_util_ssl
|
| 48 |
+
|
| 49 |
+
HTTPSConnectionPool.ConnectionCls = orig_HTTPSConnection
|
| 50 |
+
urllib3_connection.HTTPSConnection = orig_HTTPSConnection # type: ignore[misc]
|
| 51 |
+
|
| 52 |
+
urllib3_util.ALPN_PROTOCOLS = ["http/1.1"]
|
| 53 |
+
urllib3_util_ssl.ALPN_PROTOCOLS = ["http/1.1"]
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/connection.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
import re
|
| 5 |
+
import threading
|
| 6 |
+
import types
|
| 7 |
+
import typing
|
| 8 |
+
|
| 9 |
+
import h2.config
|
| 10 |
+
import h2.connection
|
| 11 |
+
import h2.events
|
| 12 |
+
|
| 13 |
+
from .._base_connection import _TYPE_BODY
|
| 14 |
+
from .._collections import HTTPHeaderDict
|
| 15 |
+
from ..connection import HTTPSConnection, _get_default_user_agent
|
| 16 |
+
from ..exceptions import ConnectionError
|
| 17 |
+
from ..response import BaseHTTPResponse
|
| 18 |
+
|
| 19 |
+
orig_HTTPSConnection = HTTPSConnection
|
| 20 |
+
|
| 21 |
+
T = typing.TypeVar("T")
|
| 22 |
+
|
| 23 |
+
log = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
RE_IS_LEGAL_HEADER_NAME = re.compile(rb"^[!#$%&'*+\-.^_`|~0-9a-z]+$")
|
| 26 |
+
RE_IS_ILLEGAL_HEADER_VALUE = re.compile(rb"[\0\x00\x0a\x0d\r\n]|^[ \r\n\t]|[ \r\n\t]$")
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _is_legal_header_name(name: bytes) -> bool:
|
| 30 |
+
"""
|
| 31 |
+
"An implementation that validates fields according to the definitions in Sections
|
| 32 |
+
5.1 and 5.5 of [HTTP] only needs an additional check that field names do not
|
| 33 |
+
include uppercase characters." (https://httpwg.org/specs/rfc9113.html#n-field-validity)
|
| 34 |
+
|
| 35 |
+
`http.client._is_legal_header_name` does not validate the field name according to the
|
| 36 |
+
HTTP 1.1 spec, so we do that here, in addition to checking for uppercase characters.
|
| 37 |
+
|
| 38 |
+
This does not allow for the `:` character in the header name, so should not
|
| 39 |
+
be used to validate pseudo-headers.
|
| 40 |
+
"""
|
| 41 |
+
return bool(RE_IS_LEGAL_HEADER_NAME.match(name))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _is_illegal_header_value(value: bytes) -> bool:
|
| 45 |
+
"""
|
| 46 |
+
"A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed
|
| 47 |
+
(ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position. A field
|
| 48 |
+
value MUST NOT start or end with an ASCII whitespace character (ASCII SP or HTAB,
|
| 49 |
+
0x20 or 0x09)." (https://httpwg.org/specs/rfc9113.html#n-field-validity)
|
| 50 |
+
"""
|
| 51 |
+
return bool(RE_IS_ILLEGAL_HEADER_VALUE.search(value))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class _LockedObject(typing.Generic[T]):
|
| 55 |
+
"""
|
| 56 |
+
A wrapper class that hides a specific object behind a lock.
|
| 57 |
+
The goal here is to provide a simple way to protect access to an object
|
| 58 |
+
that cannot safely be simultaneously accessed from multiple threads. The
|
| 59 |
+
intended use of this class is simple: take hold of it with a context
|
| 60 |
+
manager, which returns the protected object.
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
__slots__ = (
|
| 64 |
+
"lock",
|
| 65 |
+
"_obj",
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
def __init__(self, obj: T):
|
| 69 |
+
self.lock = threading.RLock()
|
| 70 |
+
self._obj = obj
|
| 71 |
+
|
| 72 |
+
def __enter__(self) -> T:
|
| 73 |
+
self.lock.acquire()
|
| 74 |
+
return self._obj
|
| 75 |
+
|
| 76 |
+
def __exit__(
|
| 77 |
+
self,
|
| 78 |
+
exc_type: type[BaseException] | None,
|
| 79 |
+
exc_val: BaseException | None,
|
| 80 |
+
exc_tb: types.TracebackType | None,
|
| 81 |
+
) -> None:
|
| 82 |
+
self.lock.release()
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class HTTP2Connection(HTTPSConnection):
|
| 86 |
+
def __init__(
|
| 87 |
+
self, host: str, port: int | None = None, **kwargs: typing.Any
|
| 88 |
+
) -> None:
|
| 89 |
+
self._h2_conn = self._new_h2_conn()
|
| 90 |
+
self._h2_stream: int | None = None
|
| 91 |
+
self._headers: list[tuple[bytes, bytes]] = []
|
| 92 |
+
|
| 93 |
+
if "proxy" in kwargs or "proxy_config" in kwargs: # Defensive:
|
| 94 |
+
raise NotImplementedError("Proxies aren't supported with HTTP/2")
|
| 95 |
+
|
| 96 |
+
super().__init__(host, port, **kwargs)
|
| 97 |
+
|
| 98 |
+
if self._tunnel_host is not None:
|
| 99 |
+
raise NotImplementedError("Tunneling isn't supported with HTTP/2")
|
| 100 |
+
|
| 101 |
+
def _new_h2_conn(self) -> _LockedObject[h2.connection.H2Connection]:
|
| 102 |
+
config = h2.config.H2Configuration(client_side=True)
|
| 103 |
+
return _LockedObject(h2.connection.H2Connection(config=config))
|
| 104 |
+
|
| 105 |
+
def connect(self) -> None:
|
| 106 |
+
super().connect()
|
| 107 |
+
with self._h2_conn as conn:
|
| 108 |
+
conn.initiate_connection()
|
| 109 |
+
if data_to_send := conn.data_to_send():
|
| 110 |
+
self.sock.sendall(data_to_send)
|
| 111 |
+
|
| 112 |
+
def putrequest( # type: ignore[override]
|
| 113 |
+
self,
|
| 114 |
+
method: str,
|
| 115 |
+
url: str,
|
| 116 |
+
**kwargs: typing.Any,
|
| 117 |
+
) -> None:
|
| 118 |
+
"""putrequest
|
| 119 |
+
This deviates from the HTTPConnection method signature since we never need to override
|
| 120 |
+
sending accept-encoding headers or the host header.
|
| 121 |
+
"""
|
| 122 |
+
if "skip_host" in kwargs:
|
| 123 |
+
raise NotImplementedError("`skip_host` isn't supported")
|
| 124 |
+
if "skip_accept_encoding" in kwargs:
|
| 125 |
+
raise NotImplementedError("`skip_accept_encoding` isn't supported")
|
| 126 |
+
|
| 127 |
+
self._request_url = url or "/"
|
| 128 |
+
self._validate_path(url) # type: ignore[attr-defined]
|
| 129 |
+
|
| 130 |
+
if ":" in self.host:
|
| 131 |
+
authority = f"[{self.host}]:{self.port or 443}"
|
| 132 |
+
else:
|
| 133 |
+
authority = f"{self.host}:{self.port or 443}"
|
| 134 |
+
|
| 135 |
+
self._headers.append((b":scheme", b"https"))
|
| 136 |
+
self._headers.append((b":method", method.encode()))
|
| 137 |
+
self._headers.append((b":authority", authority.encode()))
|
| 138 |
+
self._headers.append((b":path", url.encode()))
|
| 139 |
+
|
| 140 |
+
with self._h2_conn as conn:
|
| 141 |
+
self._h2_stream = conn.get_next_available_stream_id()
|
| 142 |
+
|
| 143 |
+
def putheader(self, header: str | bytes, *values: str | bytes) -> None: # type: ignore[override]
|
| 144 |
+
# TODO SKIPPABLE_HEADERS from urllib3 are ignored.
|
| 145 |
+
header = header.encode() if isinstance(header, str) else header
|
| 146 |
+
header = header.lower() # A lot of upstream code uses capitalized headers.
|
| 147 |
+
if not _is_legal_header_name(header):
|
| 148 |
+
raise ValueError(f"Illegal header name {str(header)}")
|
| 149 |
+
|
| 150 |
+
for value in values:
|
| 151 |
+
value = value.encode() if isinstance(value, str) else value
|
| 152 |
+
if _is_illegal_header_value(value):
|
| 153 |
+
raise ValueError(f"Illegal header value {str(value)}")
|
| 154 |
+
self._headers.append((header, value))
|
| 155 |
+
|
| 156 |
+
def endheaders(self, message_body: typing.Any = None) -> None: # type: ignore[override]
|
| 157 |
+
if self._h2_stream is None:
|
| 158 |
+
raise ConnectionError("Must call `putrequest` first.")
|
| 159 |
+
|
| 160 |
+
with self._h2_conn as conn:
|
| 161 |
+
conn.send_headers(
|
| 162 |
+
stream_id=self._h2_stream,
|
| 163 |
+
headers=self._headers,
|
| 164 |
+
end_stream=(message_body is None),
|
| 165 |
+
)
|
| 166 |
+
if data_to_send := conn.data_to_send():
|
| 167 |
+
self.sock.sendall(data_to_send)
|
| 168 |
+
self._headers = [] # Reset headers for the next request.
|
| 169 |
+
|
| 170 |
+
def send(self, data: typing.Any) -> None:
|
| 171 |
+
"""Send data to the server.
|
| 172 |
+
`data` can be: `str`, `bytes`, an iterable, or file-like objects
|
| 173 |
+
that support a .read() method.
|
| 174 |
+
"""
|
| 175 |
+
if self._h2_stream is None:
|
| 176 |
+
raise ConnectionError("Must call `putrequest` first.")
|
| 177 |
+
|
| 178 |
+
with self._h2_conn as conn:
|
| 179 |
+
if data_to_send := conn.data_to_send():
|
| 180 |
+
self.sock.sendall(data_to_send)
|
| 181 |
+
|
| 182 |
+
if hasattr(data, "read"): # file-like objects
|
| 183 |
+
while True:
|
| 184 |
+
chunk = data.read(self.blocksize)
|
| 185 |
+
if not chunk:
|
| 186 |
+
break
|
| 187 |
+
if isinstance(chunk, str):
|
| 188 |
+
chunk = chunk.encode()
|
| 189 |
+
conn.send_data(self._h2_stream, chunk, end_stream=False)
|
| 190 |
+
if data_to_send := conn.data_to_send():
|
| 191 |
+
self.sock.sendall(data_to_send)
|
| 192 |
+
conn.end_stream(self._h2_stream)
|
| 193 |
+
return
|
| 194 |
+
|
| 195 |
+
if isinstance(data, str): # str -> bytes
|
| 196 |
+
data = data.encode()
|
| 197 |
+
|
| 198 |
+
try:
|
| 199 |
+
if isinstance(data, bytes):
|
| 200 |
+
conn.send_data(self._h2_stream, data, end_stream=True)
|
| 201 |
+
if data_to_send := conn.data_to_send():
|
| 202 |
+
self.sock.sendall(data_to_send)
|
| 203 |
+
else:
|
| 204 |
+
for chunk in data:
|
| 205 |
+
conn.send_data(self._h2_stream, chunk, end_stream=False)
|
| 206 |
+
if data_to_send := conn.data_to_send():
|
| 207 |
+
self.sock.sendall(data_to_send)
|
| 208 |
+
conn.end_stream(self._h2_stream)
|
| 209 |
+
except TypeError:
|
| 210 |
+
raise TypeError(
|
| 211 |
+
"`data` should be str, bytes, iterable, or file. got %r"
|
| 212 |
+
% type(data)
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
def set_tunnel(
|
| 216 |
+
self,
|
| 217 |
+
host: str,
|
| 218 |
+
port: int | None = None,
|
| 219 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 220 |
+
scheme: str = "http",
|
| 221 |
+
) -> None:
|
| 222 |
+
raise NotImplementedError(
|
| 223 |
+
"HTTP/2 does not support setting up a tunnel through a proxy"
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
def getresponse( # type: ignore[override]
|
| 227 |
+
self,
|
| 228 |
+
) -> HTTP2Response:
|
| 229 |
+
status = None
|
| 230 |
+
data = bytearray()
|
| 231 |
+
with self._h2_conn as conn:
|
| 232 |
+
end_stream = False
|
| 233 |
+
while not end_stream:
|
| 234 |
+
# TODO: Arbitrary read value.
|
| 235 |
+
if received_data := self.sock.recv(65535):
|
| 236 |
+
events = conn.receive_data(received_data)
|
| 237 |
+
for event in events:
|
| 238 |
+
if isinstance(event, h2.events.ResponseReceived):
|
| 239 |
+
headers = HTTPHeaderDict()
|
| 240 |
+
for header, value in event.headers:
|
| 241 |
+
if header == b":status":
|
| 242 |
+
status = int(value.decode())
|
| 243 |
+
else:
|
| 244 |
+
headers.add(
|
| 245 |
+
header.decode("ascii"), value.decode("ascii")
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
elif isinstance(event, h2.events.DataReceived):
|
| 249 |
+
data += event.data
|
| 250 |
+
conn.acknowledge_received_data(
|
| 251 |
+
event.flow_controlled_length, event.stream_id
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
elif isinstance(event, h2.events.StreamEnded):
|
| 255 |
+
end_stream = True
|
| 256 |
+
|
| 257 |
+
if data_to_send := conn.data_to_send():
|
| 258 |
+
self.sock.sendall(data_to_send)
|
| 259 |
+
|
| 260 |
+
assert status is not None
|
| 261 |
+
return HTTP2Response(
|
| 262 |
+
status=status,
|
| 263 |
+
headers=headers,
|
| 264 |
+
request_url=self._request_url,
|
| 265 |
+
data=bytes(data),
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
def request( # type: ignore[override]
|
| 269 |
+
self,
|
| 270 |
+
method: str,
|
| 271 |
+
url: str,
|
| 272 |
+
body: _TYPE_BODY | None = None,
|
| 273 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 274 |
+
*,
|
| 275 |
+
preload_content: bool = True,
|
| 276 |
+
decode_content: bool = True,
|
| 277 |
+
enforce_content_length: bool = True,
|
| 278 |
+
**kwargs: typing.Any,
|
| 279 |
+
) -> None:
|
| 280 |
+
"""Send an HTTP/2 request"""
|
| 281 |
+
if "chunked" in kwargs:
|
| 282 |
+
# TODO this is often present from upstream.
|
| 283 |
+
# raise NotImplementedError("`chunked` isn't supported with HTTP/2")
|
| 284 |
+
pass
|
| 285 |
+
|
| 286 |
+
if self.sock is not None:
|
| 287 |
+
self.sock.settimeout(self.timeout)
|
| 288 |
+
|
| 289 |
+
self.putrequest(method, url)
|
| 290 |
+
|
| 291 |
+
headers = headers or {}
|
| 292 |
+
for k, v in headers.items():
|
| 293 |
+
if k.lower() == "transfer-encoding" and v == "chunked":
|
| 294 |
+
continue
|
| 295 |
+
else:
|
| 296 |
+
self.putheader(k, v)
|
| 297 |
+
|
| 298 |
+
if b"user-agent" not in dict(self._headers):
|
| 299 |
+
self.putheader(b"user-agent", _get_default_user_agent())
|
| 300 |
+
|
| 301 |
+
if body:
|
| 302 |
+
self.endheaders(message_body=body)
|
| 303 |
+
self.send(body)
|
| 304 |
+
else:
|
| 305 |
+
self.endheaders()
|
| 306 |
+
|
| 307 |
+
def close(self) -> None:
|
| 308 |
+
with self._h2_conn as conn:
|
| 309 |
+
try:
|
| 310 |
+
conn.close_connection()
|
| 311 |
+
if data := conn.data_to_send():
|
| 312 |
+
self.sock.sendall(data)
|
| 313 |
+
except Exception:
|
| 314 |
+
pass
|
| 315 |
+
|
| 316 |
+
# Reset all our HTTP/2 connection state.
|
| 317 |
+
self._h2_conn = self._new_h2_conn()
|
| 318 |
+
self._h2_stream = None
|
| 319 |
+
self._headers = []
|
| 320 |
+
|
| 321 |
+
super().close()
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
class HTTP2Response(BaseHTTPResponse):
|
| 325 |
+
# TODO: This is a woefully incomplete response object, but works for non-streaming.
|
| 326 |
+
def __init__(
|
| 327 |
+
self,
|
| 328 |
+
status: int,
|
| 329 |
+
headers: HTTPHeaderDict,
|
| 330 |
+
request_url: str,
|
| 331 |
+
data: bytes,
|
| 332 |
+
decode_content: bool = False, # TODO: support decoding
|
| 333 |
+
) -> None:
|
| 334 |
+
super().__init__(
|
| 335 |
+
status=status,
|
| 336 |
+
headers=headers,
|
| 337 |
+
# Following CPython, we map HTTP versions to major * 10 + minor integers
|
| 338 |
+
version=20,
|
| 339 |
+
version_string="HTTP/2",
|
| 340 |
+
# No reason phrase in HTTP/2
|
| 341 |
+
reason=None,
|
| 342 |
+
decode_content=decode_content,
|
| 343 |
+
request_url=request_url,
|
| 344 |
+
)
|
| 345 |
+
self._data = data
|
| 346 |
+
self.length_remaining = 0
|
| 347 |
+
|
| 348 |
+
@property
|
| 349 |
+
def data(self) -> bytes:
|
| 350 |
+
return self._data
|
| 351 |
+
|
| 352 |
+
def get_redirect_location(self) -> None:
|
| 353 |
+
return None
|
| 354 |
+
|
| 355 |
+
def close(self) -> None:
|
| 356 |
+
pass
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/http2/probe.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import threading
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class _HTTP2ProbeCache:
|
| 7 |
+
__slots__ = (
|
| 8 |
+
"_lock",
|
| 9 |
+
"_cache_locks",
|
| 10 |
+
"_cache_values",
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
def __init__(self) -> None:
|
| 14 |
+
self._lock = threading.Lock()
|
| 15 |
+
self._cache_locks: dict[tuple[str, int], threading.RLock] = {}
|
| 16 |
+
self._cache_values: dict[tuple[str, int], bool | None] = {}
|
| 17 |
+
|
| 18 |
+
def acquire_and_get(self, host: str, port: int) -> bool | None:
|
| 19 |
+
# By the end of this block we know that
|
| 20 |
+
# _cache_[values,locks] is available.
|
| 21 |
+
value = None
|
| 22 |
+
with self._lock:
|
| 23 |
+
key = (host, port)
|
| 24 |
+
try:
|
| 25 |
+
value = self._cache_values[key]
|
| 26 |
+
# If it's a known value we return right away.
|
| 27 |
+
if value is not None:
|
| 28 |
+
return value
|
| 29 |
+
except KeyError:
|
| 30 |
+
self._cache_locks[key] = threading.RLock()
|
| 31 |
+
self._cache_values[key] = None
|
| 32 |
+
|
| 33 |
+
# If the value is unknown, we acquire the lock to signal
|
| 34 |
+
# to the requesting thread that the probe is in progress
|
| 35 |
+
# or that the current thread needs to return their findings.
|
| 36 |
+
key_lock = self._cache_locks[key]
|
| 37 |
+
key_lock.acquire()
|
| 38 |
+
try:
|
| 39 |
+
# If the by the time we get the lock the value has been
|
| 40 |
+
# updated we want to return the updated value.
|
| 41 |
+
value = self._cache_values[key]
|
| 42 |
+
|
| 43 |
+
# In case an exception like KeyboardInterrupt is raised here.
|
| 44 |
+
except BaseException as e: # Defensive:
|
| 45 |
+
assert not isinstance(e, KeyError) # KeyError shouldn't be possible.
|
| 46 |
+
key_lock.release()
|
| 47 |
+
raise
|
| 48 |
+
|
| 49 |
+
return value
|
| 50 |
+
|
| 51 |
+
def set_and_release(
|
| 52 |
+
self, host: str, port: int, supports_http2: bool | None
|
| 53 |
+
) -> None:
|
| 54 |
+
key = (host, port)
|
| 55 |
+
key_lock = self._cache_locks[key]
|
| 56 |
+
with key_lock: # Uses an RLock, so can be locked again from same thread.
|
| 57 |
+
if supports_http2 is None and self._cache_values[key] is not None:
|
| 58 |
+
raise ValueError(
|
| 59 |
+
"Cannot reset HTTP/2 support for origin after value has been set."
|
| 60 |
+
) # Defensive: not expected in normal usage
|
| 61 |
+
|
| 62 |
+
self._cache_values[key] = supports_http2
|
| 63 |
+
key_lock.release()
|
| 64 |
+
|
| 65 |
+
def _values(self) -> dict[tuple[str, int], bool | None]:
|
| 66 |
+
"""This function is for testing purposes only. Gets the current state of the probe cache"""
|
| 67 |
+
with self._lock:
|
| 68 |
+
return {k: v for k, v in self._cache_values.items()}
|
| 69 |
+
|
| 70 |
+
def _reset(self) -> None:
|
| 71 |
+
"""This function is for testing purposes only. Reset the cache values"""
|
| 72 |
+
with self._lock:
|
| 73 |
+
self._cache_locks = {}
|
| 74 |
+
self._cache_values = {}
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
_HTTP2_PROBE_CACHE = _HTTP2ProbeCache()
|
| 78 |
+
|
| 79 |
+
set_and_release = _HTTP2_PROBE_CACHE.set_and_release
|
| 80 |
+
acquire_and_get = _HTTP2_PROBE_CACHE.acquire_and_get
|
| 81 |
+
_values = _HTTP2_PROBE_CACHE._values
|
| 82 |
+
_reset = _HTTP2_PROBE_CACHE._reset
|
| 83 |
+
|
| 84 |
+
__all__ = [
|
| 85 |
+
"set_and_release",
|
| 86 |
+
"acquire_and_get",
|
| 87 |
+
]
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/poolmanager.py
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import functools
|
| 4 |
+
import logging
|
| 5 |
+
import typing
|
| 6 |
+
import warnings
|
| 7 |
+
from types import TracebackType
|
| 8 |
+
from urllib.parse import urljoin
|
| 9 |
+
|
| 10 |
+
from ._collections import HTTPHeaderDict, RecentlyUsedContainer
|
| 11 |
+
from ._request_methods import RequestMethods
|
| 12 |
+
from .connection import ProxyConfig
|
| 13 |
+
from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme
|
| 14 |
+
from .exceptions import (
|
| 15 |
+
LocationValueError,
|
| 16 |
+
MaxRetryError,
|
| 17 |
+
ProxySchemeUnknown,
|
| 18 |
+
URLSchemeUnknown,
|
| 19 |
+
)
|
| 20 |
+
from .response import BaseHTTPResponse
|
| 21 |
+
from .util.connection import _TYPE_SOCKET_OPTIONS
|
| 22 |
+
from .util.proxy import connection_requires_http_tunnel
|
| 23 |
+
from .util.retry import Retry
|
| 24 |
+
from .util.timeout import Timeout
|
| 25 |
+
from .util.url import Url, parse_url
|
| 26 |
+
|
| 27 |
+
if typing.TYPE_CHECKING:
|
| 28 |
+
import ssl
|
| 29 |
+
|
| 30 |
+
from typing_extensions import Self
|
| 31 |
+
|
| 32 |
+
__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
log = logging.getLogger(__name__)
|
| 36 |
+
|
| 37 |
+
SSL_KEYWORDS = (
|
| 38 |
+
"key_file",
|
| 39 |
+
"cert_file",
|
| 40 |
+
"cert_reqs",
|
| 41 |
+
"ca_certs",
|
| 42 |
+
"ca_cert_data",
|
| 43 |
+
"ssl_version",
|
| 44 |
+
"ssl_minimum_version",
|
| 45 |
+
"ssl_maximum_version",
|
| 46 |
+
"ca_cert_dir",
|
| 47 |
+
"ssl_context",
|
| 48 |
+
"key_password",
|
| 49 |
+
"server_hostname",
|
| 50 |
+
)
|
| 51 |
+
# Default value for `blocksize` - a new parameter introduced to
|
| 52 |
+
# http.client.HTTPConnection & http.client.HTTPSConnection in Python 3.7
|
| 53 |
+
_DEFAULT_BLOCKSIZE = 16384
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class PoolKey(typing.NamedTuple):
|
| 57 |
+
"""
|
| 58 |
+
All known keyword arguments that could be provided to the pool manager, its
|
| 59 |
+
pools, or the underlying connections.
|
| 60 |
+
|
| 61 |
+
All custom key schemes should include the fields in this key at a minimum.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
key_scheme: str
|
| 65 |
+
key_host: str
|
| 66 |
+
key_port: int | None
|
| 67 |
+
key_timeout: Timeout | float | int | None
|
| 68 |
+
key_retries: Retry | bool | int | None
|
| 69 |
+
key_block: bool | None
|
| 70 |
+
key_source_address: tuple[str, int] | None
|
| 71 |
+
key_key_file: str | None
|
| 72 |
+
key_key_password: str | None
|
| 73 |
+
key_cert_file: str | None
|
| 74 |
+
key_cert_reqs: str | None
|
| 75 |
+
key_ca_certs: str | None
|
| 76 |
+
key_ca_cert_data: str | bytes | None
|
| 77 |
+
key_ssl_version: int | str | None
|
| 78 |
+
key_ssl_minimum_version: ssl.TLSVersion | None
|
| 79 |
+
key_ssl_maximum_version: ssl.TLSVersion | None
|
| 80 |
+
key_ca_cert_dir: str | None
|
| 81 |
+
key_ssl_context: ssl.SSLContext | None
|
| 82 |
+
key_maxsize: int | None
|
| 83 |
+
key_headers: frozenset[tuple[str, str]] | None
|
| 84 |
+
key__proxy: Url | None
|
| 85 |
+
key__proxy_headers: frozenset[tuple[str, str]] | None
|
| 86 |
+
key__proxy_config: ProxyConfig | None
|
| 87 |
+
key_socket_options: _TYPE_SOCKET_OPTIONS | None
|
| 88 |
+
key__socks_options: frozenset[tuple[str, str]] | None
|
| 89 |
+
key_assert_hostname: bool | str | None
|
| 90 |
+
key_assert_fingerprint: str | None
|
| 91 |
+
key_server_hostname: str | None
|
| 92 |
+
key_blocksize: int | None
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _default_key_normalizer(
|
| 96 |
+
key_class: type[PoolKey], request_context: dict[str, typing.Any]
|
| 97 |
+
) -> PoolKey:
|
| 98 |
+
"""
|
| 99 |
+
Create a pool key out of a request context dictionary.
|
| 100 |
+
|
| 101 |
+
According to RFC 3986, both the scheme and host are case-insensitive.
|
| 102 |
+
Therefore, this function normalizes both before constructing the pool
|
| 103 |
+
key for an HTTPS request. If you wish to change this behaviour, provide
|
| 104 |
+
alternate callables to ``key_fn_by_scheme``.
|
| 105 |
+
|
| 106 |
+
:param key_class:
|
| 107 |
+
The class to use when constructing the key. This should be a namedtuple
|
| 108 |
+
with the ``scheme`` and ``host`` keys at a minimum.
|
| 109 |
+
:type key_class: namedtuple
|
| 110 |
+
:param request_context:
|
| 111 |
+
A dictionary-like object that contain the context for a request.
|
| 112 |
+
:type request_context: dict
|
| 113 |
+
|
| 114 |
+
:return: A namedtuple that can be used as a connection pool key.
|
| 115 |
+
:rtype: PoolKey
|
| 116 |
+
"""
|
| 117 |
+
# Since we mutate the dictionary, make a copy first
|
| 118 |
+
context = request_context.copy()
|
| 119 |
+
context["scheme"] = context["scheme"].lower()
|
| 120 |
+
context["host"] = context["host"].lower()
|
| 121 |
+
|
| 122 |
+
# These are both dictionaries and need to be transformed into frozensets
|
| 123 |
+
for key in ("headers", "_proxy_headers", "_socks_options"):
|
| 124 |
+
if key in context and context[key] is not None:
|
| 125 |
+
context[key] = frozenset(context[key].items())
|
| 126 |
+
|
| 127 |
+
# The socket_options key may be a list and needs to be transformed into a
|
| 128 |
+
# tuple.
|
| 129 |
+
socket_opts = context.get("socket_options")
|
| 130 |
+
if socket_opts is not None:
|
| 131 |
+
context["socket_options"] = tuple(socket_opts)
|
| 132 |
+
|
| 133 |
+
# Map the kwargs to the names in the namedtuple - this is necessary since
|
| 134 |
+
# namedtuples can't have fields starting with '_'.
|
| 135 |
+
for key in list(context.keys()):
|
| 136 |
+
context["key_" + key] = context.pop(key)
|
| 137 |
+
|
| 138 |
+
# Default to ``None`` for keys missing from the context
|
| 139 |
+
for field in key_class._fields:
|
| 140 |
+
if field not in context:
|
| 141 |
+
context[field] = None
|
| 142 |
+
|
| 143 |
+
# Default key_blocksize to _DEFAULT_BLOCKSIZE if missing from the context
|
| 144 |
+
if context.get("key_blocksize") is None:
|
| 145 |
+
context["key_blocksize"] = _DEFAULT_BLOCKSIZE
|
| 146 |
+
|
| 147 |
+
return key_class(**context)
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
#: A dictionary that maps a scheme to a callable that creates a pool key.
|
| 151 |
+
#: This can be used to alter the way pool keys are constructed, if desired.
|
| 152 |
+
#: Each PoolManager makes a copy of this dictionary so they can be configured
|
| 153 |
+
#: globally here, or individually on the instance.
|
| 154 |
+
key_fn_by_scheme = {
|
| 155 |
+
"http": functools.partial(_default_key_normalizer, PoolKey),
|
| 156 |
+
"https": functools.partial(_default_key_normalizer, PoolKey),
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool}
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
class PoolManager(RequestMethods):
|
| 163 |
+
"""
|
| 164 |
+
Allows for arbitrary requests while transparently keeping track of
|
| 165 |
+
necessary connection pools for you.
|
| 166 |
+
|
| 167 |
+
:param num_pools:
|
| 168 |
+
Number of connection pools to cache before discarding the least
|
| 169 |
+
recently used pool.
|
| 170 |
+
|
| 171 |
+
:param headers:
|
| 172 |
+
Headers to include with all requests, unless other headers are given
|
| 173 |
+
explicitly.
|
| 174 |
+
|
| 175 |
+
:param \\**connection_pool_kw:
|
| 176 |
+
Additional parameters are used to create fresh
|
| 177 |
+
:class:`urllib3.connectionpool.ConnectionPool` instances.
|
| 178 |
+
|
| 179 |
+
Example:
|
| 180 |
+
|
| 181 |
+
.. code-block:: python
|
| 182 |
+
|
| 183 |
+
from pip._vendor import urllib3
|
| 184 |
+
|
| 185 |
+
http = urllib3.PoolManager(num_pools=2)
|
| 186 |
+
|
| 187 |
+
resp1 = http.request("GET", "https://google.com/")
|
| 188 |
+
resp2 = http.request("GET", "https://google.com/mail")
|
| 189 |
+
resp3 = http.request("GET", "https://yahoo.com/")
|
| 190 |
+
|
| 191 |
+
print(len(http.pools))
|
| 192 |
+
# 2
|
| 193 |
+
|
| 194 |
+
"""
|
| 195 |
+
|
| 196 |
+
proxy: Url | None = None
|
| 197 |
+
proxy_config: ProxyConfig | None = None
|
| 198 |
+
|
| 199 |
+
def __init__(
|
| 200 |
+
self,
|
| 201 |
+
num_pools: int = 10,
|
| 202 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 203 |
+
**connection_pool_kw: typing.Any,
|
| 204 |
+
) -> None:
|
| 205 |
+
super().__init__(headers)
|
| 206 |
+
# PoolManager handles redirects itself in PoolManager.urlopen().
|
| 207 |
+
# It always passes redirect=False to the underlying connection pool to
|
| 208 |
+
# suppress per-pool redirect handling. If the user supplied a non-Retry
|
| 209 |
+
# value (int/bool/etc) for retries and we let the pool normalize it
|
| 210 |
+
# while redirect=False, the resulting Retry object would have redirect
|
| 211 |
+
# handling disabled, which can interfere with PoolManager's own
|
| 212 |
+
# redirect logic. Normalize here so redirects remain governed solely by
|
| 213 |
+
# PoolManager logic.
|
| 214 |
+
if "retries" in connection_pool_kw:
|
| 215 |
+
retries = connection_pool_kw["retries"]
|
| 216 |
+
if not isinstance(retries, Retry):
|
| 217 |
+
retries = Retry.from_int(retries)
|
| 218 |
+
connection_pool_kw = connection_pool_kw.copy()
|
| 219 |
+
connection_pool_kw["retries"] = retries
|
| 220 |
+
self.connection_pool_kw = connection_pool_kw
|
| 221 |
+
|
| 222 |
+
self.pools: RecentlyUsedContainer[PoolKey, HTTPConnectionPool]
|
| 223 |
+
self.pools = RecentlyUsedContainer(num_pools)
|
| 224 |
+
|
| 225 |
+
# Locally set the pool classes and keys so other PoolManagers can
|
| 226 |
+
# override them.
|
| 227 |
+
self.pool_classes_by_scheme = pool_classes_by_scheme
|
| 228 |
+
self.key_fn_by_scheme = key_fn_by_scheme.copy()
|
| 229 |
+
|
| 230 |
+
def __enter__(self) -> Self:
|
| 231 |
+
return self
|
| 232 |
+
|
| 233 |
+
def __exit__(
|
| 234 |
+
self,
|
| 235 |
+
exc_type: type[BaseException] | None,
|
| 236 |
+
exc_val: BaseException | None,
|
| 237 |
+
exc_tb: TracebackType | None,
|
| 238 |
+
) -> typing.Literal[False]:
|
| 239 |
+
self.clear()
|
| 240 |
+
# Return False to re-raise any potential exceptions
|
| 241 |
+
return False
|
| 242 |
+
|
| 243 |
+
def _new_pool(
|
| 244 |
+
self,
|
| 245 |
+
scheme: str,
|
| 246 |
+
host: str,
|
| 247 |
+
port: int,
|
| 248 |
+
request_context: dict[str, typing.Any] | None = None,
|
| 249 |
+
) -> HTTPConnectionPool:
|
| 250 |
+
"""
|
| 251 |
+
Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and
|
| 252 |
+
any additional pool keyword arguments.
|
| 253 |
+
|
| 254 |
+
If ``request_context`` is provided, it is provided as keyword arguments
|
| 255 |
+
to the pool class used. This method is used to actually create the
|
| 256 |
+
connection pools handed out by :meth:`connection_from_url` and
|
| 257 |
+
companion methods. It is intended to be overridden for customization.
|
| 258 |
+
"""
|
| 259 |
+
pool_cls: type[HTTPConnectionPool] = self.pool_classes_by_scheme[scheme]
|
| 260 |
+
if request_context is None:
|
| 261 |
+
request_context = self.connection_pool_kw.copy()
|
| 262 |
+
|
| 263 |
+
# Default blocksize to _DEFAULT_BLOCKSIZE if missing or explicitly
|
| 264 |
+
# set to 'None' in the request_context.
|
| 265 |
+
if request_context.get("blocksize") is None:
|
| 266 |
+
request_context["blocksize"] = _DEFAULT_BLOCKSIZE
|
| 267 |
+
|
| 268 |
+
# Although the context has everything necessary to create the pool,
|
| 269 |
+
# this function has historically only used the scheme, host, and port
|
| 270 |
+
# in the positional args. When an API change is acceptable these can
|
| 271 |
+
# be removed.
|
| 272 |
+
for key in ("scheme", "host", "port"):
|
| 273 |
+
request_context.pop(key, None)
|
| 274 |
+
|
| 275 |
+
if scheme == "http":
|
| 276 |
+
for kw in SSL_KEYWORDS:
|
| 277 |
+
request_context.pop(kw, None)
|
| 278 |
+
|
| 279 |
+
return pool_cls(host, port, **request_context)
|
| 280 |
+
|
| 281 |
+
def clear(self) -> None:
|
| 282 |
+
"""
|
| 283 |
+
Empty our store of pools and direct them all to close.
|
| 284 |
+
|
| 285 |
+
This will not affect in-flight connections, but they will not be
|
| 286 |
+
re-used after completion.
|
| 287 |
+
"""
|
| 288 |
+
self.pools.clear()
|
| 289 |
+
|
| 290 |
+
def connection_from_host(
|
| 291 |
+
self,
|
| 292 |
+
host: str | None,
|
| 293 |
+
port: int | None = None,
|
| 294 |
+
scheme: str | None = "http",
|
| 295 |
+
pool_kwargs: dict[str, typing.Any] | None = None,
|
| 296 |
+
) -> HTTPConnectionPool:
|
| 297 |
+
"""
|
| 298 |
+
Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme.
|
| 299 |
+
|
| 300 |
+
If ``port`` isn't given, it will be derived from the ``scheme`` using
|
| 301 |
+
``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is
|
| 302 |
+
provided, it is merged with the instance's ``connection_pool_kw``
|
| 303 |
+
variable and used to create the new connection pool, if one is
|
| 304 |
+
needed.
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
if not host:
|
| 308 |
+
raise LocationValueError("No host specified.")
|
| 309 |
+
|
| 310 |
+
request_context = self._merge_pool_kwargs(pool_kwargs)
|
| 311 |
+
request_context["scheme"] = scheme or "http"
|
| 312 |
+
if not port:
|
| 313 |
+
port = port_by_scheme.get(request_context["scheme"].lower(), 80)
|
| 314 |
+
request_context["port"] = port
|
| 315 |
+
request_context["host"] = host
|
| 316 |
+
|
| 317 |
+
return self.connection_from_context(request_context)
|
| 318 |
+
|
| 319 |
+
def connection_from_context(
|
| 320 |
+
self, request_context: dict[str, typing.Any]
|
| 321 |
+
) -> HTTPConnectionPool:
|
| 322 |
+
"""
|
| 323 |
+
Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context.
|
| 324 |
+
|
| 325 |
+
``request_context`` must at least contain the ``scheme`` key and its
|
| 326 |
+
value must be a key in ``key_fn_by_scheme`` instance variable.
|
| 327 |
+
"""
|
| 328 |
+
if "strict" in request_context:
|
| 329 |
+
warnings.warn(
|
| 330 |
+
"The 'strict' parameter is no longer needed on Python 3+. "
|
| 331 |
+
"This will raise an error in urllib3 v3.0.",
|
| 332 |
+
FutureWarning,
|
| 333 |
+
)
|
| 334 |
+
request_context.pop("strict")
|
| 335 |
+
|
| 336 |
+
scheme = request_context["scheme"].lower()
|
| 337 |
+
pool_key_constructor = self.key_fn_by_scheme.get(scheme)
|
| 338 |
+
if not pool_key_constructor:
|
| 339 |
+
raise URLSchemeUnknown(scheme)
|
| 340 |
+
pool_key = pool_key_constructor(request_context)
|
| 341 |
+
|
| 342 |
+
return self.connection_from_pool_key(pool_key, request_context=request_context)
|
| 343 |
+
|
| 344 |
+
def connection_from_pool_key(
|
| 345 |
+
self, pool_key: PoolKey, request_context: dict[str, typing.Any]
|
| 346 |
+
) -> HTTPConnectionPool:
|
| 347 |
+
"""
|
| 348 |
+
Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key.
|
| 349 |
+
|
| 350 |
+
``pool_key`` should be a namedtuple that only contains immutable
|
| 351 |
+
objects. At a minimum it must have the ``scheme``, ``host``, and
|
| 352 |
+
``port`` fields.
|
| 353 |
+
"""
|
| 354 |
+
with self.pools.lock:
|
| 355 |
+
# If the scheme, host, or port doesn't match existing open
|
| 356 |
+
# connections, open a new ConnectionPool.
|
| 357 |
+
pool = self.pools.get(pool_key)
|
| 358 |
+
if pool:
|
| 359 |
+
return pool
|
| 360 |
+
|
| 361 |
+
# Make a fresh ConnectionPool of the desired type
|
| 362 |
+
scheme = request_context["scheme"]
|
| 363 |
+
host = request_context["host"]
|
| 364 |
+
port = request_context["port"]
|
| 365 |
+
pool = self._new_pool(scheme, host, port, request_context=request_context)
|
| 366 |
+
self.pools[pool_key] = pool
|
| 367 |
+
|
| 368 |
+
return pool
|
| 369 |
+
|
| 370 |
+
def connection_from_url(
|
| 371 |
+
self, url: str, pool_kwargs: dict[str, typing.Any] | None = None
|
| 372 |
+
) -> HTTPConnectionPool:
|
| 373 |
+
"""
|
| 374 |
+
Similar to :func:`urllib3.connectionpool.connection_from_url`.
|
| 375 |
+
|
| 376 |
+
If ``pool_kwargs`` is not provided and a new pool needs to be
|
| 377 |
+
constructed, ``self.connection_pool_kw`` is used to initialize
|
| 378 |
+
the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs``
|
| 379 |
+
is provided, it is used instead. Note that if a new pool does not
|
| 380 |
+
need to be created for the request, the provided ``pool_kwargs`` are
|
| 381 |
+
not used.
|
| 382 |
+
"""
|
| 383 |
+
u = parse_url(url)
|
| 384 |
+
return self.connection_from_host(
|
| 385 |
+
u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs
|
| 386 |
+
)
|
| 387 |
+
|
| 388 |
+
def _merge_pool_kwargs(
|
| 389 |
+
self, override: dict[str, typing.Any] | None
|
| 390 |
+
) -> dict[str, typing.Any]:
|
| 391 |
+
"""
|
| 392 |
+
Merge a dictionary of override values for self.connection_pool_kw.
|
| 393 |
+
|
| 394 |
+
This does not modify self.connection_pool_kw and returns a new dict.
|
| 395 |
+
Any keys in the override dictionary with a value of ``None`` are
|
| 396 |
+
removed from the merged dictionary.
|
| 397 |
+
"""
|
| 398 |
+
base_pool_kwargs = self.connection_pool_kw.copy()
|
| 399 |
+
if override:
|
| 400 |
+
for key, value in override.items():
|
| 401 |
+
if value is None:
|
| 402 |
+
try:
|
| 403 |
+
del base_pool_kwargs[key]
|
| 404 |
+
except KeyError:
|
| 405 |
+
pass
|
| 406 |
+
else:
|
| 407 |
+
base_pool_kwargs[key] = value
|
| 408 |
+
return base_pool_kwargs
|
| 409 |
+
|
| 410 |
+
def _proxy_requires_url_absolute_form(self, parsed_url: Url) -> bool:
|
| 411 |
+
"""
|
| 412 |
+
Indicates if the proxy requires the complete destination URL in the
|
| 413 |
+
request. Normally this is only needed when not using an HTTP CONNECT
|
| 414 |
+
tunnel.
|
| 415 |
+
"""
|
| 416 |
+
if self.proxy is None:
|
| 417 |
+
return False
|
| 418 |
+
|
| 419 |
+
return not connection_requires_http_tunnel(
|
| 420 |
+
self.proxy, self.proxy_config, parsed_url.scheme
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
def urlopen( # type: ignore[override]
|
| 424 |
+
self, method: str, url: str, redirect: bool = True, **kw: typing.Any
|
| 425 |
+
) -> BaseHTTPResponse:
|
| 426 |
+
"""
|
| 427 |
+
Same as :meth:`urllib3.HTTPConnectionPool.urlopen`
|
| 428 |
+
with custom cross-host redirect logic and only sends the request-uri
|
| 429 |
+
portion of the ``url``.
|
| 430 |
+
|
| 431 |
+
The given ``url`` parameter must be absolute, such that an appropriate
|
| 432 |
+
:class:`urllib3.connectionpool.ConnectionPool` can be chosen for it.
|
| 433 |
+
"""
|
| 434 |
+
u = parse_url(url)
|
| 435 |
+
|
| 436 |
+
if u.scheme is None:
|
| 437 |
+
warnings.warn(
|
| 438 |
+
"URLs without a scheme (ie 'https://') are deprecated and will raise an error "
|
| 439 |
+
"in urllib3 v3.0. To avoid this FutureWarning ensure all URLs "
|
| 440 |
+
"start with 'https://' or 'http://'. Read more in this issue: "
|
| 441 |
+
"https://github.com/urllib3/urllib3/issues/2920",
|
| 442 |
+
category=FutureWarning,
|
| 443 |
+
stacklevel=2,
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme)
|
| 447 |
+
|
| 448 |
+
kw["assert_same_host"] = False
|
| 449 |
+
kw["redirect"] = False
|
| 450 |
+
|
| 451 |
+
if "headers" not in kw:
|
| 452 |
+
kw["headers"] = self.headers
|
| 453 |
+
|
| 454 |
+
if self._proxy_requires_url_absolute_form(u):
|
| 455 |
+
response = conn.urlopen(method, url, **kw)
|
| 456 |
+
else:
|
| 457 |
+
response = conn.urlopen(method, u.request_uri, **kw)
|
| 458 |
+
|
| 459 |
+
redirect_location = redirect and response.get_redirect_location()
|
| 460 |
+
if not redirect_location:
|
| 461 |
+
return response
|
| 462 |
+
|
| 463 |
+
# Support relative URLs for redirecting.
|
| 464 |
+
redirect_location = urljoin(url, redirect_location)
|
| 465 |
+
|
| 466 |
+
if response.status == 303:
|
| 467 |
+
# Change the method according to RFC 9110, Section 15.4.4.
|
| 468 |
+
method = "GET"
|
| 469 |
+
# And lose the body not to transfer anything sensitive.
|
| 470 |
+
kw["body"] = None
|
| 471 |
+
kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change()
|
| 472 |
+
|
| 473 |
+
retries = kw.get("retries", response.retries)
|
| 474 |
+
if not isinstance(retries, Retry):
|
| 475 |
+
retries = Retry.from_int(retries, redirect=redirect)
|
| 476 |
+
|
| 477 |
+
# Strip headers marked as unsafe to forward to the redirected location.
|
| 478 |
+
# Check remove_headers_on_redirect to avoid a potential network call within
|
| 479 |
+
# conn.is_same_host() which may use socket.gethostbyname() in the future.
|
| 480 |
+
if retries.remove_headers_on_redirect and not conn.is_same_host(
|
| 481 |
+
redirect_location
|
| 482 |
+
):
|
| 483 |
+
new_headers = kw["headers"].copy()
|
| 484 |
+
for header in kw["headers"]:
|
| 485 |
+
if header.lower() in retries.remove_headers_on_redirect:
|
| 486 |
+
new_headers.pop(header, None)
|
| 487 |
+
kw["headers"] = new_headers
|
| 488 |
+
|
| 489 |
+
try:
|
| 490 |
+
retries = retries.increment(method, url, response=response, _pool=conn)
|
| 491 |
+
except MaxRetryError:
|
| 492 |
+
if retries.raise_on_redirect:
|
| 493 |
+
response.drain_conn()
|
| 494 |
+
raise
|
| 495 |
+
return response
|
| 496 |
+
|
| 497 |
+
kw["retries"] = retries
|
| 498 |
+
kw["redirect"] = redirect
|
| 499 |
+
|
| 500 |
+
log.info("Redirecting %s -> %s", url, redirect_location)
|
| 501 |
+
|
| 502 |
+
response.drain_conn()
|
| 503 |
+
return self.urlopen(method, redirect_location, **kw)
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
class ProxyManager(PoolManager):
|
| 507 |
+
"""
|
| 508 |
+
Behaves just like :class:`PoolManager`, but sends all requests through
|
| 509 |
+
the defined proxy, using the CONNECT method for HTTPS URLs.
|
| 510 |
+
|
| 511 |
+
:param proxy_url:
|
| 512 |
+
The URL of the proxy to be used.
|
| 513 |
+
|
| 514 |
+
:param proxy_headers:
|
| 515 |
+
A dictionary containing headers that will be sent to the proxy. In case
|
| 516 |
+
of HTTP they are being sent with each request, while in the
|
| 517 |
+
HTTPS/CONNECT case they are sent only once. Could be used for proxy
|
| 518 |
+
authentication.
|
| 519 |
+
|
| 520 |
+
:param proxy_ssl_context:
|
| 521 |
+
The proxy SSL context is used to establish the TLS connection to the
|
| 522 |
+
proxy when using HTTPS proxies.
|
| 523 |
+
|
| 524 |
+
:param use_forwarding_for_https:
|
| 525 |
+
(Defaults to False) If set to True will forward requests to the HTTPS
|
| 526 |
+
proxy to be made on behalf of the client instead of creating a TLS
|
| 527 |
+
tunnel via the CONNECT method. **Enabling this flag means that request
|
| 528 |
+
and response headers and content will be visible from the HTTPS proxy**
|
| 529 |
+
whereas tunneling keeps request and response headers and content
|
| 530 |
+
private. IP address, target hostname, SNI, and port are always visible
|
| 531 |
+
to an HTTPS proxy even when this flag is disabled.
|
| 532 |
+
|
| 533 |
+
:param proxy_assert_hostname:
|
| 534 |
+
The hostname of the certificate to verify against.
|
| 535 |
+
|
| 536 |
+
:param proxy_assert_fingerprint:
|
| 537 |
+
The fingerprint of the certificate to verify against.
|
| 538 |
+
|
| 539 |
+
Example:
|
| 540 |
+
|
| 541 |
+
.. code-block:: python
|
| 542 |
+
|
| 543 |
+
from pip._vendor import urllib3
|
| 544 |
+
|
| 545 |
+
proxy = urllib3.ProxyManager("https://localhost:3128/")
|
| 546 |
+
|
| 547 |
+
resp1 = proxy.request("GET", "http://google.com/")
|
| 548 |
+
resp2 = proxy.request("GET", "http://httpbin.org/")
|
| 549 |
+
|
| 550 |
+
# One pool was shared by both plain HTTP requests.
|
| 551 |
+
print(len(proxy.pools))
|
| 552 |
+
# 1
|
| 553 |
+
|
| 554 |
+
resp3 = proxy.request("GET", "https://httpbin.org/")
|
| 555 |
+
resp4 = proxy.request("GET", "https://twitter.com/")
|
| 556 |
+
|
| 557 |
+
# A separate pool was added for each HTTPS target.
|
| 558 |
+
print(len(proxy.pools))
|
| 559 |
+
# 3
|
| 560 |
+
|
| 561 |
+
"""
|
| 562 |
+
|
| 563 |
+
def __init__(
|
| 564 |
+
self,
|
| 565 |
+
proxy_url: str,
|
| 566 |
+
num_pools: int = 10,
|
| 567 |
+
headers: typing.Mapping[str, str] | None = None,
|
| 568 |
+
proxy_headers: typing.Mapping[str, str] | None = None,
|
| 569 |
+
proxy_ssl_context: ssl.SSLContext | None = None,
|
| 570 |
+
use_forwarding_for_https: bool = False,
|
| 571 |
+
proxy_assert_hostname: None | str | typing.Literal[False] = None,
|
| 572 |
+
proxy_assert_fingerprint: str | None = None,
|
| 573 |
+
**connection_pool_kw: typing.Any,
|
| 574 |
+
) -> None:
|
| 575 |
+
if isinstance(proxy_url, HTTPConnectionPool):
|
| 576 |
+
str_proxy_url = f"{proxy_url.scheme}://{proxy_url.host}:{proxy_url.port}"
|
| 577 |
+
else:
|
| 578 |
+
str_proxy_url = proxy_url
|
| 579 |
+
proxy = parse_url(str_proxy_url)
|
| 580 |
+
|
| 581 |
+
if proxy.scheme not in ("http", "https"):
|
| 582 |
+
raise ProxySchemeUnknown(proxy.scheme)
|
| 583 |
+
|
| 584 |
+
if not proxy.port:
|
| 585 |
+
port = port_by_scheme.get(proxy.scheme, 80)
|
| 586 |
+
proxy = proxy._replace(port=port)
|
| 587 |
+
|
| 588 |
+
self.proxy = proxy
|
| 589 |
+
self.proxy_headers = proxy_headers or {}
|
| 590 |
+
self.proxy_ssl_context = proxy_ssl_context
|
| 591 |
+
self.proxy_config = ProxyConfig(
|
| 592 |
+
proxy_ssl_context,
|
| 593 |
+
use_forwarding_for_https,
|
| 594 |
+
proxy_assert_hostname,
|
| 595 |
+
proxy_assert_fingerprint,
|
| 596 |
+
)
|
| 597 |
+
|
| 598 |
+
connection_pool_kw["_proxy"] = self.proxy
|
| 599 |
+
connection_pool_kw["_proxy_headers"] = self.proxy_headers
|
| 600 |
+
connection_pool_kw["_proxy_config"] = self.proxy_config
|
| 601 |
+
|
| 602 |
+
super().__init__(num_pools, headers, **connection_pool_kw)
|
| 603 |
+
|
| 604 |
+
def connection_from_host(
|
| 605 |
+
self,
|
| 606 |
+
host: str | None,
|
| 607 |
+
port: int | None = None,
|
| 608 |
+
scheme: str | None = "http",
|
| 609 |
+
pool_kwargs: dict[str, typing.Any] | None = None,
|
| 610 |
+
) -> HTTPConnectionPool:
|
| 611 |
+
if scheme == "https":
|
| 612 |
+
return super().connection_from_host(
|
| 613 |
+
host, port, scheme, pool_kwargs=pool_kwargs
|
| 614 |
+
)
|
| 615 |
+
|
| 616 |
+
return super().connection_from_host(
|
| 617 |
+
self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs # type: ignore[union-attr]
|
| 618 |
+
)
|
| 619 |
+
|
| 620 |
+
def _set_proxy_headers(
|
| 621 |
+
self, url: str, headers: typing.Mapping[str, str] | None = None
|
| 622 |
+
) -> typing.Mapping[str, str]:
|
| 623 |
+
"""
|
| 624 |
+
Sets headers needed by proxies: specifically, the Accept and Host
|
| 625 |
+
headers. Only sets headers not provided by the user.
|
| 626 |
+
"""
|
| 627 |
+
headers_ = {"Accept": "*/*"}
|
| 628 |
+
|
| 629 |
+
netloc = parse_url(url).netloc
|
| 630 |
+
if netloc:
|
| 631 |
+
headers_["Host"] = netloc
|
| 632 |
+
|
| 633 |
+
if headers:
|
| 634 |
+
headers_.update(headers)
|
| 635 |
+
return headers_
|
| 636 |
+
|
| 637 |
+
def urlopen( # type: ignore[override]
|
| 638 |
+
self, method: str, url: str, redirect: bool = True, **kw: typing.Any
|
| 639 |
+
) -> BaseHTTPResponse:
|
| 640 |
+
"Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute."
|
| 641 |
+
u = parse_url(url)
|
| 642 |
+
if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme):
|
| 643 |
+
# For connections using HTTP CONNECT, httplib sets the necessary
|
| 644 |
+
# headers on the CONNECT to the proxy. If we're not using CONNECT,
|
| 645 |
+
# we'll definitely need to set 'Host' at the very least.
|
| 646 |
+
headers = kw.get("headers", self.headers)
|
| 647 |
+
kw["headers"] = self._set_proxy_headers(url, headers)
|
| 648 |
+
|
| 649 |
+
return super().urlopen(method, url, redirect=redirect, **kw)
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
def proxy_from_url(url: str, **kw: typing.Any) -> ProxyManager:
|
| 653 |
+
return ProxyManager(proxy_url=url, **kw)
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/py.typed
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Instruct type checkers to look for inline type annotations in this package.
|
| 2 |
+
# See PEP 561.
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/response.py
ADDED
|
@@ -0,0 +1,1487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections
|
| 4 |
+
import io
|
| 5 |
+
import json as _json
|
| 6 |
+
import logging
|
| 7 |
+
import socket
|
| 8 |
+
import sys
|
| 9 |
+
import typing
|
| 10 |
+
import warnings
|
| 11 |
+
import zlib
|
| 12 |
+
from contextlib import contextmanager
|
| 13 |
+
from http.client import HTTPMessage as _HttplibHTTPMessage
|
| 14 |
+
from http.client import HTTPResponse as _HttplibHTTPResponse
|
| 15 |
+
from socket import timeout as SocketTimeout
|
| 16 |
+
|
| 17 |
+
if typing.TYPE_CHECKING:
|
| 18 |
+
from ._base_connection import BaseHTTPConnection
|
| 19 |
+
|
| 20 |
+
brotli = None
|
| 21 |
+
|
| 22 |
+
from . import util
|
| 23 |
+
from ._base_connection import _TYPE_BODY
|
| 24 |
+
from ._collections import HTTPHeaderDict
|
| 25 |
+
from .connection import BaseSSLError, HTTPConnection, HTTPException
|
| 26 |
+
from .exceptions import (
|
| 27 |
+
BodyNotHttplibCompatible,
|
| 28 |
+
DecodeError,
|
| 29 |
+
DependencyWarning,
|
| 30 |
+
HTTPError,
|
| 31 |
+
IncompleteRead,
|
| 32 |
+
InvalidChunkLength,
|
| 33 |
+
InvalidHeader,
|
| 34 |
+
ProtocolError,
|
| 35 |
+
ReadTimeoutError,
|
| 36 |
+
ResponseNotChunked,
|
| 37 |
+
SSLError,
|
| 38 |
+
)
|
| 39 |
+
from .util.response import is_fp_closed, is_response_to_head
|
| 40 |
+
from .util.retry import Retry
|
| 41 |
+
|
| 42 |
+
if typing.TYPE_CHECKING:
|
| 43 |
+
from .connectionpool import HTTPConnectionPool
|
| 44 |
+
|
| 45 |
+
log = logging.getLogger(__name__)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class ContentDecoder:
|
| 49 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 50 |
+
raise NotImplementedError()
|
| 51 |
+
|
| 52 |
+
@property
|
| 53 |
+
def has_unconsumed_tail(self) -> bool:
|
| 54 |
+
raise NotImplementedError()
|
| 55 |
+
|
| 56 |
+
def flush(self) -> bytes:
|
| 57 |
+
raise NotImplementedError()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class DeflateDecoder(ContentDecoder):
|
| 61 |
+
def __init__(self) -> None:
|
| 62 |
+
self._first_try = True
|
| 63 |
+
self._first_try_data = b""
|
| 64 |
+
self._unfed_data = b""
|
| 65 |
+
self._obj = zlib.decompressobj()
|
| 66 |
+
|
| 67 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 68 |
+
data = self._unfed_data + data
|
| 69 |
+
self._unfed_data = b""
|
| 70 |
+
if not data and not self._obj.unconsumed_tail:
|
| 71 |
+
return data
|
| 72 |
+
original_max_length = max_length
|
| 73 |
+
if original_max_length < 0:
|
| 74 |
+
max_length = 0
|
| 75 |
+
elif original_max_length == 0:
|
| 76 |
+
# We should not pass 0 to the zlib decompressor because 0 is
|
| 77 |
+
# the default value that will make zlib decompress without a
|
| 78 |
+
# length limit.
|
| 79 |
+
# Data should be stored for subsequent calls.
|
| 80 |
+
self._unfed_data = data
|
| 81 |
+
return b""
|
| 82 |
+
|
| 83 |
+
# Subsequent calls always reuse `self._obj`. zlib requires
|
| 84 |
+
# passing the unconsumed tail if decompression is to continue.
|
| 85 |
+
if not self._first_try:
|
| 86 |
+
return self._obj.decompress(
|
| 87 |
+
self._obj.unconsumed_tail + data, max_length=max_length
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
# First call tries with RFC 1950 ZLIB format.
|
| 91 |
+
self._first_try_data += data
|
| 92 |
+
try:
|
| 93 |
+
decompressed = self._obj.decompress(data, max_length=max_length)
|
| 94 |
+
if decompressed:
|
| 95 |
+
self._first_try = False
|
| 96 |
+
self._first_try_data = b""
|
| 97 |
+
return decompressed
|
| 98 |
+
# On failure, it falls back to RFC 1951 DEFLATE format.
|
| 99 |
+
except zlib.error:
|
| 100 |
+
self._first_try = False
|
| 101 |
+
self._obj = zlib.decompressobj(-zlib.MAX_WBITS)
|
| 102 |
+
try:
|
| 103 |
+
return self.decompress(
|
| 104 |
+
self._first_try_data, max_length=original_max_length
|
| 105 |
+
)
|
| 106 |
+
finally:
|
| 107 |
+
self._first_try_data = b""
|
| 108 |
+
|
| 109 |
+
@property
|
| 110 |
+
def has_unconsumed_tail(self) -> bool:
|
| 111 |
+
return bool(self._unfed_data) or (
|
| 112 |
+
bool(self._obj.unconsumed_tail) and not self._first_try
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
def flush(self) -> bytes:
|
| 116 |
+
return self._obj.flush()
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class GzipDecoderState:
|
| 120 |
+
FIRST_MEMBER = 0
|
| 121 |
+
OTHER_MEMBERS = 1
|
| 122 |
+
SWALLOW_DATA = 2
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class GzipDecoder(ContentDecoder):
|
| 126 |
+
def __init__(self) -> None:
|
| 127 |
+
self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
|
| 128 |
+
self._state = GzipDecoderState.FIRST_MEMBER
|
| 129 |
+
self._unconsumed_tail = b""
|
| 130 |
+
|
| 131 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 132 |
+
ret = bytearray()
|
| 133 |
+
if self._state == GzipDecoderState.SWALLOW_DATA:
|
| 134 |
+
return bytes(ret)
|
| 135 |
+
|
| 136 |
+
if max_length == 0:
|
| 137 |
+
# We should not pass 0 to the zlib decompressor because 0 is
|
| 138 |
+
# the default value that will make zlib decompress without a
|
| 139 |
+
# length limit.
|
| 140 |
+
# Data should be stored for subsequent calls.
|
| 141 |
+
self._unconsumed_tail += data
|
| 142 |
+
return b""
|
| 143 |
+
|
| 144 |
+
# zlib requires passing the unconsumed tail to the subsequent
|
| 145 |
+
# call if decompression is to continue.
|
| 146 |
+
data = self._unconsumed_tail + data
|
| 147 |
+
if not data and self._obj.eof:
|
| 148 |
+
return bytes(ret)
|
| 149 |
+
|
| 150 |
+
while True:
|
| 151 |
+
try:
|
| 152 |
+
ret += self._obj.decompress(
|
| 153 |
+
data, max_length=max(max_length - len(ret), 0)
|
| 154 |
+
)
|
| 155 |
+
except zlib.error:
|
| 156 |
+
previous_state = self._state
|
| 157 |
+
# Ignore data after the first error
|
| 158 |
+
self._state = GzipDecoderState.SWALLOW_DATA
|
| 159 |
+
self._unconsumed_tail = b""
|
| 160 |
+
if previous_state == GzipDecoderState.OTHER_MEMBERS:
|
| 161 |
+
# Allow trailing garbage acceptable in other gzip clients
|
| 162 |
+
return bytes(ret)
|
| 163 |
+
raise
|
| 164 |
+
|
| 165 |
+
self._unconsumed_tail = data = (
|
| 166 |
+
self._obj.unconsumed_tail or self._obj.unused_data
|
| 167 |
+
)
|
| 168 |
+
if max_length > 0 and len(ret) >= max_length:
|
| 169 |
+
break
|
| 170 |
+
|
| 171 |
+
if not data:
|
| 172 |
+
return bytes(ret)
|
| 173 |
+
# When the end of a gzip member is reached, a new decompressor
|
| 174 |
+
# must be created for unused (possibly future) data.
|
| 175 |
+
if self._obj.eof:
|
| 176 |
+
self._state = GzipDecoderState.OTHER_MEMBERS
|
| 177 |
+
self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
|
| 178 |
+
|
| 179 |
+
return bytes(ret)
|
| 180 |
+
|
| 181 |
+
@property
|
| 182 |
+
def has_unconsumed_tail(self) -> bool:
|
| 183 |
+
return bool(self._unconsumed_tail)
|
| 184 |
+
|
| 185 |
+
def flush(self) -> bytes:
|
| 186 |
+
return self._obj.flush()
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
if brotli is not None:
|
| 190 |
+
|
| 191 |
+
class BrotliDecoder(ContentDecoder):
|
| 192 |
+
# Supports both 'brotlipy' and 'Brotli' packages
|
| 193 |
+
# since they share an import name. The top branches
|
| 194 |
+
# are for 'brotlipy' and bottom branches for 'Brotli'
|
| 195 |
+
def __init__(self) -> None:
|
| 196 |
+
self._obj = brotli.Decompressor()
|
| 197 |
+
if hasattr(self._obj, "decompress"):
|
| 198 |
+
setattr(self, "_decompress", self._obj.decompress)
|
| 199 |
+
else:
|
| 200 |
+
setattr(self, "_decompress", self._obj.process)
|
| 201 |
+
|
| 202 |
+
# Requires Brotli >= 1.2.0 for `output_buffer_limit`.
|
| 203 |
+
def _decompress(self, data: bytes, output_buffer_limit: int = -1) -> bytes:
|
| 204 |
+
raise NotImplementedError()
|
| 205 |
+
|
| 206 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 207 |
+
try:
|
| 208 |
+
if max_length > 0:
|
| 209 |
+
return self._decompress(data, output_buffer_limit=max_length)
|
| 210 |
+
else:
|
| 211 |
+
return self._decompress(data)
|
| 212 |
+
except TypeError:
|
| 213 |
+
# Fallback for Brotli/brotlicffi/brotlipy versions without
|
| 214 |
+
# the `output_buffer_limit` parameter.
|
| 215 |
+
warnings.warn(
|
| 216 |
+
"Brotli >= 1.2.0 is required to prevent decompression bombs.",
|
| 217 |
+
DependencyWarning,
|
| 218 |
+
)
|
| 219 |
+
return self._decompress(data)
|
| 220 |
+
|
| 221 |
+
@property
|
| 222 |
+
def has_unconsumed_tail(self) -> bool:
|
| 223 |
+
try:
|
| 224 |
+
return not self._obj.can_accept_more_data()
|
| 225 |
+
except AttributeError:
|
| 226 |
+
return False
|
| 227 |
+
|
| 228 |
+
def flush(self) -> bytes:
|
| 229 |
+
if hasattr(self._obj, "flush"):
|
| 230 |
+
return self._obj.flush() # type: ignore[no-any-return]
|
| 231 |
+
return b""
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
try:
|
| 235 |
+
if sys.version_info >= (3, 14):
|
| 236 |
+
from compression import zstd
|
| 237 |
+
else:
|
| 238 |
+
from backports import zstd
|
| 239 |
+
except ImportError:
|
| 240 |
+
HAS_ZSTD = False
|
| 241 |
+
else:
|
| 242 |
+
HAS_ZSTD = True
|
| 243 |
+
|
| 244 |
+
class ZstdDecoder(ContentDecoder):
|
| 245 |
+
def __init__(self) -> None:
|
| 246 |
+
self._obj = zstd.ZstdDecompressor()
|
| 247 |
+
|
| 248 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 249 |
+
if not data and not self.has_unconsumed_tail:
|
| 250 |
+
return b""
|
| 251 |
+
if self._obj.eof:
|
| 252 |
+
data = self._obj.unused_data + data
|
| 253 |
+
self._obj = zstd.ZstdDecompressor()
|
| 254 |
+
part = self._obj.decompress(data, max_length=max_length)
|
| 255 |
+
length = len(part)
|
| 256 |
+
data_parts = [part]
|
| 257 |
+
# Every loop iteration is supposed to read data from a separate frame.
|
| 258 |
+
# The loop breaks when:
|
| 259 |
+
# - enough data is read;
|
| 260 |
+
# - no more unused data is available;
|
| 261 |
+
# - end of the last read frame has not been reached (i.e.,
|
| 262 |
+
# more data has to be fed).
|
| 263 |
+
while (
|
| 264 |
+
self._obj.eof
|
| 265 |
+
and self._obj.unused_data
|
| 266 |
+
and (max_length < 0 or length < max_length)
|
| 267 |
+
):
|
| 268 |
+
unused_data = self._obj.unused_data
|
| 269 |
+
if not self._obj.needs_input:
|
| 270 |
+
self._obj = zstd.ZstdDecompressor()
|
| 271 |
+
part = self._obj.decompress(
|
| 272 |
+
unused_data,
|
| 273 |
+
max_length=(max_length - length) if max_length > 0 else -1,
|
| 274 |
+
)
|
| 275 |
+
if part_length := len(part):
|
| 276 |
+
data_parts.append(part)
|
| 277 |
+
length += part_length
|
| 278 |
+
elif self._obj.needs_input:
|
| 279 |
+
break
|
| 280 |
+
return b"".join(data_parts)
|
| 281 |
+
|
| 282 |
+
@property
|
| 283 |
+
def has_unconsumed_tail(self) -> bool:
|
| 284 |
+
return not (self._obj.needs_input or self._obj.eof) or bool(
|
| 285 |
+
self._obj.unused_data
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
def flush(self) -> bytes:
|
| 289 |
+
if not self._obj.eof:
|
| 290 |
+
raise DecodeError("Zstandard data is incomplete")
|
| 291 |
+
return b""
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
class MultiDecoder(ContentDecoder):
|
| 295 |
+
"""
|
| 296 |
+
From RFC7231:
|
| 297 |
+
If one or more encodings have been applied to a representation, the
|
| 298 |
+
sender that applied the encodings MUST generate a Content-Encoding
|
| 299 |
+
header field that lists the content codings in the order in which
|
| 300 |
+
they were applied.
|
| 301 |
+
"""
|
| 302 |
+
|
| 303 |
+
# Maximum allowed number of chained HTTP encodings in the
|
| 304 |
+
# Content-Encoding header.
|
| 305 |
+
max_decode_links = 5
|
| 306 |
+
|
| 307 |
+
def __init__(self, modes: str) -> None:
|
| 308 |
+
encodings = [m.strip() for m in modes.split(",")]
|
| 309 |
+
if len(encodings) > self.max_decode_links:
|
| 310 |
+
raise DecodeError(
|
| 311 |
+
"Too many content encodings in the chain: "
|
| 312 |
+
f"{len(encodings)} > {self.max_decode_links}"
|
| 313 |
+
)
|
| 314 |
+
self._decoders = [_get_decoder(e) for e in encodings]
|
| 315 |
+
|
| 316 |
+
def flush(self) -> bytes:
|
| 317 |
+
return self._decoders[0].flush()
|
| 318 |
+
|
| 319 |
+
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
|
| 320 |
+
if max_length <= 0:
|
| 321 |
+
for d in reversed(self._decoders):
|
| 322 |
+
data = d.decompress(data)
|
| 323 |
+
return data
|
| 324 |
+
|
| 325 |
+
ret = bytearray()
|
| 326 |
+
# Every while loop iteration goes through all decoders once.
|
| 327 |
+
# It exits when enough data is read or no more data can be read.
|
| 328 |
+
# It is possible that the while loop iteration does not produce
|
| 329 |
+
# any data because we retrieve up to `max_length` from every
|
| 330 |
+
# decoder, and the amount of bytes may be insufficient for the
|
| 331 |
+
# next decoder to produce enough/any output.
|
| 332 |
+
while True:
|
| 333 |
+
any_data = False
|
| 334 |
+
for d in reversed(self._decoders):
|
| 335 |
+
data = d.decompress(data, max_length=max_length - len(ret))
|
| 336 |
+
if data:
|
| 337 |
+
any_data = True
|
| 338 |
+
# We should not break when no data is returned because
|
| 339 |
+
# next decoders may produce data even with empty input.
|
| 340 |
+
ret += data
|
| 341 |
+
if not any_data or len(ret) >= max_length:
|
| 342 |
+
return bytes(ret)
|
| 343 |
+
data = b""
|
| 344 |
+
|
| 345 |
+
@property
|
| 346 |
+
def has_unconsumed_tail(self) -> bool:
|
| 347 |
+
return any(d.has_unconsumed_tail for d in self._decoders)
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def _get_decoder(mode: str) -> ContentDecoder:
|
| 351 |
+
if "," in mode:
|
| 352 |
+
return MultiDecoder(mode)
|
| 353 |
+
|
| 354 |
+
# According to RFC 9110 section 8.4.1.3, recipients should
|
| 355 |
+
# consider x-gzip equivalent to gzip
|
| 356 |
+
if mode in ("gzip", "x-gzip"):
|
| 357 |
+
return GzipDecoder()
|
| 358 |
+
|
| 359 |
+
if brotli is not None and mode == "br":
|
| 360 |
+
return BrotliDecoder()
|
| 361 |
+
|
| 362 |
+
if HAS_ZSTD and mode == "zstd":
|
| 363 |
+
return ZstdDecoder()
|
| 364 |
+
|
| 365 |
+
return DeflateDecoder()
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
class BytesQueueBuffer:
|
| 369 |
+
"""Memory-efficient bytes buffer
|
| 370 |
+
|
| 371 |
+
To return decoded data in read() and still follow the BufferedIOBase API, we need a
|
| 372 |
+
buffer to always return the correct amount of bytes.
|
| 373 |
+
|
| 374 |
+
This buffer should be filled using calls to put()
|
| 375 |
+
|
| 376 |
+
Our maximum memory usage is determined by the sum of the size of:
|
| 377 |
+
|
| 378 |
+
* self.buffer, which contains the full data
|
| 379 |
+
* the largest chunk that we will copy in get()
|
| 380 |
+
"""
|
| 381 |
+
|
| 382 |
+
def __init__(self) -> None:
|
| 383 |
+
self.buffer: typing.Deque[bytes | memoryview[bytes]] = collections.deque()
|
| 384 |
+
self._size: int = 0
|
| 385 |
+
|
| 386 |
+
def __len__(self) -> int:
|
| 387 |
+
return self._size
|
| 388 |
+
|
| 389 |
+
def put(self, data: bytes) -> None:
|
| 390 |
+
self.buffer.append(data)
|
| 391 |
+
self._size += len(data)
|
| 392 |
+
|
| 393 |
+
def get(self, n: int) -> bytes:
|
| 394 |
+
if n == 0:
|
| 395 |
+
return b""
|
| 396 |
+
elif not self.buffer:
|
| 397 |
+
raise RuntimeError("buffer is empty")
|
| 398 |
+
elif n < 0:
|
| 399 |
+
raise ValueError("n should be > 0")
|
| 400 |
+
|
| 401 |
+
if len(self.buffer[0]) == n and isinstance(self.buffer[0], bytes):
|
| 402 |
+
self._size -= n
|
| 403 |
+
return self.buffer.popleft()
|
| 404 |
+
|
| 405 |
+
fetched = 0
|
| 406 |
+
ret = io.BytesIO()
|
| 407 |
+
while fetched < n:
|
| 408 |
+
remaining = n - fetched
|
| 409 |
+
chunk = self.buffer.popleft()
|
| 410 |
+
chunk_length = len(chunk)
|
| 411 |
+
if remaining < chunk_length:
|
| 412 |
+
chunk = memoryview(chunk)
|
| 413 |
+
left_chunk, right_chunk = chunk[:remaining], chunk[remaining:]
|
| 414 |
+
ret.write(left_chunk)
|
| 415 |
+
self.buffer.appendleft(right_chunk)
|
| 416 |
+
self._size -= remaining
|
| 417 |
+
break
|
| 418 |
+
else:
|
| 419 |
+
ret.write(chunk)
|
| 420 |
+
self._size -= chunk_length
|
| 421 |
+
fetched += chunk_length
|
| 422 |
+
|
| 423 |
+
if not self.buffer:
|
| 424 |
+
break
|
| 425 |
+
|
| 426 |
+
return ret.getvalue()
|
| 427 |
+
|
| 428 |
+
def get_all(self) -> bytes:
|
| 429 |
+
buffer = self.buffer
|
| 430 |
+
if not buffer:
|
| 431 |
+
assert self._size == 0
|
| 432 |
+
return b""
|
| 433 |
+
if len(buffer) == 1:
|
| 434 |
+
result = buffer.pop()
|
| 435 |
+
if isinstance(result, memoryview):
|
| 436 |
+
result = result.tobytes()
|
| 437 |
+
else:
|
| 438 |
+
ret = io.BytesIO()
|
| 439 |
+
ret.writelines(buffer.popleft() for _ in range(len(buffer)))
|
| 440 |
+
result = ret.getvalue()
|
| 441 |
+
self._size = 0
|
| 442 |
+
return result
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
class BaseHTTPResponse(io.IOBase):
|
| 446 |
+
CONTENT_DECODERS = ["gzip", "x-gzip", "deflate"]
|
| 447 |
+
if brotli is not None:
|
| 448 |
+
CONTENT_DECODERS += ["br"]
|
| 449 |
+
if HAS_ZSTD:
|
| 450 |
+
CONTENT_DECODERS += ["zstd"]
|
| 451 |
+
REDIRECT_STATUSES = [301, 302, 303, 307, 308]
|
| 452 |
+
|
| 453 |
+
DECODER_ERROR_CLASSES: tuple[type[Exception], ...] = (IOError, zlib.error)
|
| 454 |
+
if brotli is not None:
|
| 455 |
+
DECODER_ERROR_CLASSES += (brotli.error,)
|
| 456 |
+
|
| 457 |
+
if HAS_ZSTD:
|
| 458 |
+
DECODER_ERROR_CLASSES += (zstd.ZstdError,)
|
| 459 |
+
|
| 460 |
+
def __init__(
|
| 461 |
+
self,
|
| 462 |
+
*,
|
| 463 |
+
headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None,
|
| 464 |
+
status: int,
|
| 465 |
+
version: int,
|
| 466 |
+
version_string: str,
|
| 467 |
+
reason: str | None,
|
| 468 |
+
decode_content: bool,
|
| 469 |
+
request_url: str | None,
|
| 470 |
+
retries: Retry | None = None,
|
| 471 |
+
) -> None:
|
| 472 |
+
if isinstance(headers, HTTPHeaderDict):
|
| 473 |
+
self.headers = headers
|
| 474 |
+
else:
|
| 475 |
+
self.headers = HTTPHeaderDict(headers) # type: ignore[arg-type]
|
| 476 |
+
self.status = status
|
| 477 |
+
self.version = version
|
| 478 |
+
self.version_string = version_string
|
| 479 |
+
self.reason = reason
|
| 480 |
+
self.decode_content = decode_content
|
| 481 |
+
self._has_decoded_content = False
|
| 482 |
+
self._request_url: str | None = request_url
|
| 483 |
+
self.retries = retries
|
| 484 |
+
|
| 485 |
+
self.chunked = False
|
| 486 |
+
tr_enc = self.headers.get("transfer-encoding", "").lower()
|
| 487 |
+
# Don't incur the penalty of creating a list and then discarding it
|
| 488 |
+
encodings = (enc.strip() for enc in tr_enc.split(","))
|
| 489 |
+
if "chunked" in encodings:
|
| 490 |
+
self.chunked = True
|
| 491 |
+
|
| 492 |
+
self._decoder: ContentDecoder | None = None
|
| 493 |
+
self.length_remaining: int | None
|
| 494 |
+
|
| 495 |
+
def get_redirect_location(self) -> str | None | typing.Literal[False]:
|
| 496 |
+
"""
|
| 497 |
+
Should we redirect and where to?
|
| 498 |
+
|
| 499 |
+
:returns: Truthy redirect location string if we got a redirect status
|
| 500 |
+
code and valid location. ``None`` if redirect status and no
|
| 501 |
+
location. ``False`` if not a redirect status code.
|
| 502 |
+
"""
|
| 503 |
+
if self.status in self.REDIRECT_STATUSES:
|
| 504 |
+
return self.headers.get("location")
|
| 505 |
+
return False
|
| 506 |
+
|
| 507 |
+
@property
|
| 508 |
+
def data(self) -> bytes:
|
| 509 |
+
raise NotImplementedError()
|
| 510 |
+
|
| 511 |
+
def json(self) -> typing.Any:
|
| 512 |
+
"""
|
| 513 |
+
Deserializes the body of the HTTP response as a Python object.
|
| 514 |
+
|
| 515 |
+
The body of the HTTP response must be encoded using UTF-8, as per
|
| 516 |
+
`RFC 8529 Section 8.1 <https://www.rfc-editor.org/rfc/rfc8259#section-8.1>`_.
|
| 517 |
+
|
| 518 |
+
To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to
|
| 519 |
+
your custom decoder instead.
|
| 520 |
+
|
| 521 |
+
If the body of the HTTP response is not decodable to UTF-8, a
|
| 522 |
+
`UnicodeDecodeError` will be raised. If the body of the HTTP response is not a
|
| 523 |
+
valid JSON document, a `json.JSONDecodeError` will be raised.
|
| 524 |
+
|
| 525 |
+
Read more :ref:`here <json_content>`.
|
| 526 |
+
|
| 527 |
+
:returns: The body of the HTTP response as a Python object.
|
| 528 |
+
"""
|
| 529 |
+
data = self.data.decode("utf-8")
|
| 530 |
+
return _json.loads(data)
|
| 531 |
+
|
| 532 |
+
@property
|
| 533 |
+
def url(self) -> str | None:
|
| 534 |
+
raise NotImplementedError()
|
| 535 |
+
|
| 536 |
+
@url.setter
|
| 537 |
+
def url(self, url: str | None) -> None:
|
| 538 |
+
raise NotImplementedError()
|
| 539 |
+
|
| 540 |
+
@property
|
| 541 |
+
def connection(self) -> BaseHTTPConnection | None:
|
| 542 |
+
raise NotImplementedError()
|
| 543 |
+
|
| 544 |
+
@property
|
| 545 |
+
def retries(self) -> Retry | None:
|
| 546 |
+
return self._retries
|
| 547 |
+
|
| 548 |
+
@retries.setter
|
| 549 |
+
def retries(self, retries: Retry | None) -> None:
|
| 550 |
+
# Override the request_url if retries has a redirect location.
|
| 551 |
+
if retries is not None and retries.history:
|
| 552 |
+
self.url = retries.history[-1].redirect_location
|
| 553 |
+
self._retries = retries
|
| 554 |
+
|
| 555 |
+
def stream(
|
| 556 |
+
self, amt: int | None = 2**16, decode_content: bool | None = None
|
| 557 |
+
) -> typing.Iterator[bytes]:
|
| 558 |
+
raise NotImplementedError()
|
| 559 |
+
|
| 560 |
+
def read(
|
| 561 |
+
self,
|
| 562 |
+
amt: int | None = None,
|
| 563 |
+
decode_content: bool | None = None,
|
| 564 |
+
cache_content: bool = False,
|
| 565 |
+
) -> bytes:
|
| 566 |
+
raise NotImplementedError()
|
| 567 |
+
|
| 568 |
+
def read1(
|
| 569 |
+
self,
|
| 570 |
+
amt: int | None = None,
|
| 571 |
+
decode_content: bool | None = None,
|
| 572 |
+
) -> bytes:
|
| 573 |
+
raise NotImplementedError()
|
| 574 |
+
|
| 575 |
+
def read_chunked(
|
| 576 |
+
self,
|
| 577 |
+
amt: int | None = None,
|
| 578 |
+
decode_content: bool | None = None,
|
| 579 |
+
) -> typing.Iterator[bytes]:
|
| 580 |
+
raise NotImplementedError()
|
| 581 |
+
|
| 582 |
+
def release_conn(self) -> None:
|
| 583 |
+
raise NotImplementedError()
|
| 584 |
+
|
| 585 |
+
def drain_conn(self) -> None:
|
| 586 |
+
raise NotImplementedError()
|
| 587 |
+
|
| 588 |
+
def shutdown(self) -> None:
|
| 589 |
+
raise NotImplementedError()
|
| 590 |
+
|
| 591 |
+
def close(self) -> None:
|
| 592 |
+
raise NotImplementedError()
|
| 593 |
+
|
| 594 |
+
def _init_decoder(self) -> None:
|
| 595 |
+
"""
|
| 596 |
+
Set-up the _decoder attribute if necessary.
|
| 597 |
+
"""
|
| 598 |
+
# Note: content-encoding value should be case-insensitive, per RFC 7230
|
| 599 |
+
# Section 3.2
|
| 600 |
+
content_encoding = self.headers.get("content-encoding", "").lower()
|
| 601 |
+
if self._decoder is None:
|
| 602 |
+
if content_encoding in self.CONTENT_DECODERS:
|
| 603 |
+
self._decoder = _get_decoder(content_encoding)
|
| 604 |
+
elif "," in content_encoding:
|
| 605 |
+
encodings = [
|
| 606 |
+
e.strip()
|
| 607 |
+
for e in content_encoding.split(",")
|
| 608 |
+
if e.strip() in self.CONTENT_DECODERS
|
| 609 |
+
]
|
| 610 |
+
if encodings:
|
| 611 |
+
self._decoder = _get_decoder(content_encoding)
|
| 612 |
+
|
| 613 |
+
def _decode(
|
| 614 |
+
self,
|
| 615 |
+
data: bytes,
|
| 616 |
+
decode_content: bool | None,
|
| 617 |
+
flush_decoder: bool,
|
| 618 |
+
max_length: int | None = None,
|
| 619 |
+
) -> bytes:
|
| 620 |
+
"""
|
| 621 |
+
Decode the data passed in and potentially flush the decoder.
|
| 622 |
+
"""
|
| 623 |
+
if not decode_content:
|
| 624 |
+
if self._has_decoded_content:
|
| 625 |
+
raise RuntimeError(
|
| 626 |
+
"Calling read(decode_content=False) is not supported after "
|
| 627 |
+
"read(decode_content=True) was called."
|
| 628 |
+
)
|
| 629 |
+
return data
|
| 630 |
+
|
| 631 |
+
if max_length is None or flush_decoder:
|
| 632 |
+
max_length = -1
|
| 633 |
+
|
| 634 |
+
try:
|
| 635 |
+
if self._decoder:
|
| 636 |
+
data = self._decoder.decompress(data, max_length=max_length)
|
| 637 |
+
self._has_decoded_content = True
|
| 638 |
+
except self.DECODER_ERROR_CLASSES as e:
|
| 639 |
+
content_encoding = self.headers.get("content-encoding", "").lower()
|
| 640 |
+
raise DecodeError(
|
| 641 |
+
"Received response with content-encoding: %s, but "
|
| 642 |
+
"failed to decode it." % content_encoding,
|
| 643 |
+
e,
|
| 644 |
+
) from e
|
| 645 |
+
if flush_decoder:
|
| 646 |
+
data += self._flush_decoder()
|
| 647 |
+
|
| 648 |
+
return data
|
| 649 |
+
|
| 650 |
+
def _flush_decoder(self) -> bytes:
|
| 651 |
+
"""
|
| 652 |
+
Flushes the decoder. Should only be called if the decoder is actually
|
| 653 |
+
being used.
|
| 654 |
+
"""
|
| 655 |
+
if self._decoder:
|
| 656 |
+
return self._decoder.decompress(b"") + self._decoder.flush()
|
| 657 |
+
return b""
|
| 658 |
+
|
| 659 |
+
# Compatibility methods for `io` module
|
| 660 |
+
def readinto(self, b: bytearray | memoryview[int]) -> int:
|
| 661 |
+
temp = self.read(len(b))
|
| 662 |
+
if len(temp) == 0:
|
| 663 |
+
return 0
|
| 664 |
+
else:
|
| 665 |
+
b[: len(temp)] = temp
|
| 666 |
+
return len(temp)
|
| 667 |
+
|
| 668 |
+
# Methods used by dependent libraries
|
| 669 |
+
def getheaders(self) -> HTTPHeaderDict:
|
| 670 |
+
return self.headers
|
| 671 |
+
|
| 672 |
+
def getheader(self, name: str, default: str | None = None) -> str | None:
|
| 673 |
+
return self.headers.get(name, default)
|
| 674 |
+
|
| 675 |
+
# Compatibility method for http.cookiejar
|
| 676 |
+
def info(self) -> HTTPHeaderDict:
|
| 677 |
+
return self.headers
|
| 678 |
+
|
| 679 |
+
def geturl(self) -> str | None:
|
| 680 |
+
return self.url
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
class HTTPResponse(BaseHTTPResponse):
|
| 684 |
+
"""
|
| 685 |
+
HTTP Response container.
|
| 686 |
+
|
| 687 |
+
Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is
|
| 688 |
+
loaded and decoded on-demand when the ``data`` property is accessed. This
|
| 689 |
+
class is also compatible with the Python standard library's :mod:`io`
|
| 690 |
+
module, and can hence be treated as a readable object in the context of that
|
| 691 |
+
framework.
|
| 692 |
+
|
| 693 |
+
Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`:
|
| 694 |
+
|
| 695 |
+
:param preload_content:
|
| 696 |
+
If True, the response's body will be preloaded during construction.
|
| 697 |
+
|
| 698 |
+
:param decode_content:
|
| 699 |
+
If True, will attempt to decode the body based on the
|
| 700 |
+
'content-encoding' header.
|
| 701 |
+
|
| 702 |
+
:param original_response:
|
| 703 |
+
When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse`
|
| 704 |
+
object, it's convenient to include the original for debug purposes. It's
|
| 705 |
+
otherwise unused.
|
| 706 |
+
|
| 707 |
+
:param retries:
|
| 708 |
+
The retries contains the last :class:`~urllib3.util.retry.Retry` that
|
| 709 |
+
was used during the request.
|
| 710 |
+
|
| 711 |
+
:param enforce_content_length:
|
| 712 |
+
Enforce content length checking. Body returned by server must match
|
| 713 |
+
value of Content-Length header, if present. Otherwise, raise error.
|
| 714 |
+
"""
|
| 715 |
+
|
| 716 |
+
def __init__(
|
| 717 |
+
self,
|
| 718 |
+
body: _TYPE_BODY = "",
|
| 719 |
+
headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None,
|
| 720 |
+
status: int = 0,
|
| 721 |
+
version: int = 0,
|
| 722 |
+
version_string: str = "HTTP/?",
|
| 723 |
+
reason: str | None = None,
|
| 724 |
+
preload_content: bool = True,
|
| 725 |
+
decode_content: bool = True,
|
| 726 |
+
original_response: _HttplibHTTPResponse | None = None,
|
| 727 |
+
pool: HTTPConnectionPool | None = None,
|
| 728 |
+
connection: HTTPConnection | None = None,
|
| 729 |
+
msg: _HttplibHTTPMessage | None = None,
|
| 730 |
+
retries: Retry | None = None,
|
| 731 |
+
enforce_content_length: bool = True,
|
| 732 |
+
request_method: str | None = None,
|
| 733 |
+
request_url: str | None = None,
|
| 734 |
+
auto_close: bool = True,
|
| 735 |
+
sock_shutdown: typing.Callable[[int], None] | None = None,
|
| 736 |
+
) -> None:
|
| 737 |
+
super().__init__(
|
| 738 |
+
headers=headers,
|
| 739 |
+
status=status,
|
| 740 |
+
version=version,
|
| 741 |
+
version_string=version_string,
|
| 742 |
+
reason=reason,
|
| 743 |
+
decode_content=decode_content,
|
| 744 |
+
request_url=request_url,
|
| 745 |
+
retries=retries,
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
+
self.enforce_content_length = enforce_content_length
|
| 749 |
+
self.auto_close = auto_close
|
| 750 |
+
|
| 751 |
+
self._body = None
|
| 752 |
+
self._uncached_read_occurred = False
|
| 753 |
+
self._fp: _HttplibHTTPResponse | None = None
|
| 754 |
+
self._original_response = original_response
|
| 755 |
+
self._fp_bytes_read = 0
|
| 756 |
+
self.msg = msg
|
| 757 |
+
|
| 758 |
+
if body and isinstance(body, (str, bytes)):
|
| 759 |
+
self._body = body
|
| 760 |
+
|
| 761 |
+
self._pool = pool
|
| 762 |
+
self._connection = connection
|
| 763 |
+
|
| 764 |
+
if hasattr(body, "read"):
|
| 765 |
+
self._fp = body # type: ignore[assignment]
|
| 766 |
+
self._sock_shutdown = sock_shutdown
|
| 767 |
+
|
| 768 |
+
# Are we using the chunked-style of transfer encoding?
|
| 769 |
+
self.chunk_left: int | None = None
|
| 770 |
+
|
| 771 |
+
# Determine length of response
|
| 772 |
+
self.length_remaining = self._init_length(request_method)
|
| 773 |
+
|
| 774 |
+
# Used to return the correct amount of bytes for partial read()s
|
| 775 |
+
self._decoded_buffer = BytesQueueBuffer()
|
| 776 |
+
|
| 777 |
+
# If requested, preload the body.
|
| 778 |
+
if preload_content and not self._body:
|
| 779 |
+
self._body = self.read(decode_content=decode_content)
|
| 780 |
+
|
| 781 |
+
def release_conn(self) -> None:
|
| 782 |
+
if not self._pool or not self._connection:
|
| 783 |
+
return None
|
| 784 |
+
|
| 785 |
+
self._pool._put_conn(self._connection)
|
| 786 |
+
self._connection = None
|
| 787 |
+
|
| 788 |
+
def drain_conn(self) -> None:
|
| 789 |
+
"""
|
| 790 |
+
Read and discard any remaining HTTP response data in the response connection.
|
| 791 |
+
|
| 792 |
+
Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
|
| 793 |
+
"""
|
| 794 |
+
try:
|
| 795 |
+
self._raw_read()
|
| 796 |
+
except (HTTPError, OSError, BaseSSLError, HTTPException):
|
| 797 |
+
pass
|
| 798 |
+
if self._has_decoded_content:
|
| 799 |
+
# `_raw_read` skips decompression, so we should clean up the
|
| 800 |
+
# decoder to avoid keeping unnecessary data in memory.
|
| 801 |
+
self._decoded_buffer = BytesQueueBuffer()
|
| 802 |
+
self._decoder = None
|
| 803 |
+
|
| 804 |
+
@property
|
| 805 |
+
def data(self) -> bytes:
|
| 806 |
+
# For backwards-compat with earlier urllib3 0.4 and earlier.
|
| 807 |
+
if self._body:
|
| 808 |
+
return self._body # type: ignore[return-value]
|
| 809 |
+
|
| 810 |
+
if self._fp:
|
| 811 |
+
return self.read(cache_content=True)
|
| 812 |
+
|
| 813 |
+
return None # type: ignore[return-value]
|
| 814 |
+
|
| 815 |
+
@property
|
| 816 |
+
def connection(self) -> HTTPConnection | None:
|
| 817 |
+
return self._connection
|
| 818 |
+
|
| 819 |
+
def isclosed(self) -> bool:
|
| 820 |
+
return is_fp_closed(self._fp)
|
| 821 |
+
|
| 822 |
+
def tell(self) -> int:
|
| 823 |
+
"""
|
| 824 |
+
Obtain the number of bytes pulled over the wire so far. May differ from
|
| 825 |
+
the amount of content returned by :meth:`HTTPResponse.read`
|
| 826 |
+
if bytes are encoded on the wire (e.g, compressed).
|
| 827 |
+
"""
|
| 828 |
+
return self._fp_bytes_read
|
| 829 |
+
|
| 830 |
+
def _init_length(self, request_method: str | None) -> int | None:
|
| 831 |
+
"""
|
| 832 |
+
Set initial length value for Response content if available.
|
| 833 |
+
"""
|
| 834 |
+
length: int | None
|
| 835 |
+
content_length: str | None = self.headers.get("content-length")
|
| 836 |
+
|
| 837 |
+
if content_length is not None:
|
| 838 |
+
if self.chunked:
|
| 839 |
+
# This Response will fail with an IncompleteRead if it can't be
|
| 840 |
+
# received as chunked. This method falls back to attempt reading
|
| 841 |
+
# the response before raising an exception.
|
| 842 |
+
log.warning(
|
| 843 |
+
"Received response with both Content-Length and "
|
| 844 |
+
"Transfer-Encoding set. This is expressly forbidden "
|
| 845 |
+
"by RFC 7230 sec 3.3.2. Ignoring Content-Length and "
|
| 846 |
+
"attempting to process response as Transfer-Encoding: "
|
| 847 |
+
"chunked."
|
| 848 |
+
)
|
| 849 |
+
return None
|
| 850 |
+
|
| 851 |
+
try:
|
| 852 |
+
# RFC 7230 section 3.3.2 specifies multiple content lengths can
|
| 853 |
+
# be sent in a single Content-Length header
|
| 854 |
+
# (e.g. Content-Length: 42, 42). This line ensures the values
|
| 855 |
+
# are all valid ints and that as long as the `set` length is 1,
|
| 856 |
+
# all values are the same. Otherwise, the header is invalid.
|
| 857 |
+
lengths = {int(val) for val in content_length.split(",")}
|
| 858 |
+
if len(lengths) > 1:
|
| 859 |
+
raise InvalidHeader(
|
| 860 |
+
"Content-Length contained multiple "
|
| 861 |
+
"unmatching values (%s)" % content_length
|
| 862 |
+
)
|
| 863 |
+
length = lengths.pop()
|
| 864 |
+
except ValueError:
|
| 865 |
+
length = None
|
| 866 |
+
else:
|
| 867 |
+
if length < 0:
|
| 868 |
+
length = None
|
| 869 |
+
|
| 870 |
+
else: # if content_length is None
|
| 871 |
+
length = None
|
| 872 |
+
|
| 873 |
+
# Convert status to int for comparison
|
| 874 |
+
# In some cases, httplib returns a status of "_UNKNOWN"
|
| 875 |
+
try:
|
| 876 |
+
status = int(self.status)
|
| 877 |
+
except ValueError:
|
| 878 |
+
status = 0
|
| 879 |
+
|
| 880 |
+
# Check for responses that shouldn't include a body
|
| 881 |
+
if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD":
|
| 882 |
+
length = 0
|
| 883 |
+
|
| 884 |
+
return length
|
| 885 |
+
|
| 886 |
+
@contextmanager
|
| 887 |
+
def _error_catcher(self) -> typing.Generator[None]:
|
| 888 |
+
"""
|
| 889 |
+
Catch low-level python exceptions, instead re-raising urllib3
|
| 890 |
+
variants, so that low-level exceptions are not leaked in the
|
| 891 |
+
high-level api.
|
| 892 |
+
|
| 893 |
+
On exit, release the connection back to the pool.
|
| 894 |
+
"""
|
| 895 |
+
clean_exit = False
|
| 896 |
+
|
| 897 |
+
try:
|
| 898 |
+
try:
|
| 899 |
+
yield
|
| 900 |
+
|
| 901 |
+
except SocketTimeout as e:
|
| 902 |
+
# FIXME: Ideally we'd like to include the url in the ReadTimeoutError but
|
| 903 |
+
# there is yet no clean way to get at it from this context.
|
| 904 |
+
raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type]
|
| 905 |
+
|
| 906 |
+
except BaseSSLError as e:
|
| 907 |
+
# SSL errors related to framing/MAC get wrapped and reraised here
|
| 908 |
+
raise SSLError(e) from e
|
| 909 |
+
|
| 910 |
+
except IncompleteRead as e:
|
| 911 |
+
if (
|
| 912 |
+
e.expected is not None
|
| 913 |
+
and e.partial is not None
|
| 914 |
+
and e.expected == -e.partial
|
| 915 |
+
):
|
| 916 |
+
arg = "Response may not contain content."
|
| 917 |
+
else:
|
| 918 |
+
arg = f"Connection broken: {e!r}"
|
| 919 |
+
raise ProtocolError(arg, e) from e
|
| 920 |
+
|
| 921 |
+
except (HTTPException, OSError) as e:
|
| 922 |
+
raise ProtocolError(f"Connection broken: {e!r}", e) from e
|
| 923 |
+
|
| 924 |
+
# If no exception is thrown, we should avoid cleaning up
|
| 925 |
+
# unnecessarily.
|
| 926 |
+
clean_exit = True
|
| 927 |
+
finally:
|
| 928 |
+
# If we didn't terminate cleanly, we need to throw away our
|
| 929 |
+
# connection.
|
| 930 |
+
if not clean_exit:
|
| 931 |
+
# The response may not be closed but we're not going to use it
|
| 932 |
+
# anymore so close it now to ensure that the connection is
|
| 933 |
+
# released back to the pool.
|
| 934 |
+
if self._original_response:
|
| 935 |
+
self._original_response.close()
|
| 936 |
+
|
| 937 |
+
# Closing the response may not actually be sufficient to close
|
| 938 |
+
# everything, so if we have a hold of the connection close that
|
| 939 |
+
# too.
|
| 940 |
+
if self._connection:
|
| 941 |
+
self._connection.close()
|
| 942 |
+
|
| 943 |
+
# If we hold the original response but it's closed now, we should
|
| 944 |
+
# return the connection back to the pool.
|
| 945 |
+
if self._original_response and self._original_response.isclosed():
|
| 946 |
+
self.release_conn()
|
| 947 |
+
|
| 948 |
+
def _fp_read(
|
| 949 |
+
self,
|
| 950 |
+
amt: int | None = None,
|
| 951 |
+
*,
|
| 952 |
+
read1: bool = False,
|
| 953 |
+
) -> bytes:
|
| 954 |
+
"""
|
| 955 |
+
Read a response with the thought that reading the number of bytes
|
| 956 |
+
larger than can fit in a 32-bit int at a time via SSL in some
|
| 957 |
+
known cases leads to an overflow error that has to be prevented
|
| 958 |
+
if `amt` or `self.length_remaining` indicate that a problem may
|
| 959 |
+
happen.
|
| 960 |
+
|
| 961 |
+
This happens to urllib3 injected with pyOpenSSL-backed SSL-support.
|
| 962 |
+
"""
|
| 963 |
+
assert self._fp
|
| 964 |
+
c_int_max = 2**31 - 1
|
| 965 |
+
if (
|
| 966 |
+
(amt and amt > c_int_max)
|
| 967 |
+
or (
|
| 968 |
+
amt is None
|
| 969 |
+
and self.length_remaining
|
| 970 |
+
and self.length_remaining > c_int_max
|
| 971 |
+
)
|
| 972 |
+
) and util.IS_PYOPENSSL:
|
| 973 |
+
if read1:
|
| 974 |
+
return self._fp.read1(c_int_max)
|
| 975 |
+
buffer = io.BytesIO()
|
| 976 |
+
# Besides `max_chunk_amt` being a maximum chunk size, it
|
| 977 |
+
# affects memory overhead of reading a response by this
|
| 978 |
+
# method in CPython.
|
| 979 |
+
# `c_int_max` equal to 2 GiB - 1 byte is the actual maximum
|
| 980 |
+
# chunk size that does not lead to an overflow error, but
|
| 981 |
+
# 256 MiB is a compromise.
|
| 982 |
+
max_chunk_amt = 2**28
|
| 983 |
+
while amt is None or amt != 0:
|
| 984 |
+
if amt is not None:
|
| 985 |
+
chunk_amt = min(amt, max_chunk_amt)
|
| 986 |
+
amt -= chunk_amt
|
| 987 |
+
else:
|
| 988 |
+
chunk_amt = max_chunk_amt
|
| 989 |
+
data = self._fp.read(chunk_amt)
|
| 990 |
+
if not data:
|
| 991 |
+
break
|
| 992 |
+
buffer.write(data)
|
| 993 |
+
del data # to reduce peak memory usage by `max_chunk_amt`.
|
| 994 |
+
return buffer.getvalue()
|
| 995 |
+
elif read1:
|
| 996 |
+
return self._fp.read1(amt) if amt is not None else self._fp.read1()
|
| 997 |
+
else:
|
| 998 |
+
# StringIO doesn't like amt=None
|
| 999 |
+
return self._fp.read(amt) if amt is not None else self._fp.read()
|
| 1000 |
+
|
| 1001 |
+
def _raw_read(
|
| 1002 |
+
self,
|
| 1003 |
+
amt: int | None = None,
|
| 1004 |
+
*,
|
| 1005 |
+
read1: bool = False,
|
| 1006 |
+
) -> bytes:
|
| 1007 |
+
"""
|
| 1008 |
+
Reads `amt` of bytes from the socket.
|
| 1009 |
+
"""
|
| 1010 |
+
if self._fp is None:
|
| 1011 |
+
return None # type: ignore[return-value]
|
| 1012 |
+
|
| 1013 |
+
fp_closed = getattr(self._fp, "closed", False)
|
| 1014 |
+
|
| 1015 |
+
with self._error_catcher():
|
| 1016 |
+
data = self._fp_read(amt, read1=read1) if not fp_closed else b""
|
| 1017 |
+
if amt is not None and amt != 0 and not data:
|
| 1018 |
+
# Platform-specific: Buggy versions of Python.
|
| 1019 |
+
# Close the connection when no data is returned
|
| 1020 |
+
#
|
| 1021 |
+
# This is redundant to what httplib/http.client _should_
|
| 1022 |
+
# already do. However, versions of python released before
|
| 1023 |
+
# December 15, 2012 (http://bugs.python.org/issue16298) do
|
| 1024 |
+
# not properly close the connection in all cases. There is
|
| 1025 |
+
# no harm in redundantly calling close.
|
| 1026 |
+
self._fp.close()
|
| 1027 |
+
if (
|
| 1028 |
+
self.enforce_content_length
|
| 1029 |
+
and self.length_remaining is not None
|
| 1030 |
+
and self.length_remaining != 0
|
| 1031 |
+
):
|
| 1032 |
+
# This is an edge case that httplib failed to cover due
|
| 1033 |
+
# to concerns of backward compatibility. We're
|
| 1034 |
+
# addressing it here to make sure IncompleteRead is
|
| 1035 |
+
# raised during streaming, so all calls with incorrect
|
| 1036 |
+
# Content-Length are caught.
|
| 1037 |
+
raise IncompleteRead(self._fp_bytes_read, self.length_remaining)
|
| 1038 |
+
elif read1 and (
|
| 1039 |
+
(amt != 0 and not data) or self.length_remaining == len(data)
|
| 1040 |
+
):
|
| 1041 |
+
# All data has been read, but `self._fp.read1` in
|
| 1042 |
+
# CPython 3.12 and older doesn't always close
|
| 1043 |
+
# `http.client.HTTPResponse`, so we close it here.
|
| 1044 |
+
# See https://github.com/python/cpython/issues/113199
|
| 1045 |
+
self._fp.close()
|
| 1046 |
+
|
| 1047 |
+
if data:
|
| 1048 |
+
self._fp_bytes_read += len(data)
|
| 1049 |
+
if self.length_remaining is not None:
|
| 1050 |
+
self.length_remaining -= len(data)
|
| 1051 |
+
return data
|
| 1052 |
+
|
| 1053 |
+
def read(
|
| 1054 |
+
self,
|
| 1055 |
+
amt: int | None = None,
|
| 1056 |
+
decode_content: bool | None = None,
|
| 1057 |
+
cache_content: bool = False,
|
| 1058 |
+
) -> bytes:
|
| 1059 |
+
"""
|
| 1060 |
+
Similar to :meth:`http.client.HTTPResponse.read`, but with two additional
|
| 1061 |
+
parameters: ``decode_content`` and ``cache_content``.
|
| 1062 |
+
|
| 1063 |
+
:param amt:
|
| 1064 |
+
How much of the content to read. If specified, caching is skipped
|
| 1065 |
+
because it doesn't make sense to cache partial content as the full
|
| 1066 |
+
response.
|
| 1067 |
+
|
| 1068 |
+
:param decode_content:
|
| 1069 |
+
If True, will attempt to decode the body based on the
|
| 1070 |
+
'content-encoding' header.
|
| 1071 |
+
|
| 1072 |
+
:param cache_content:
|
| 1073 |
+
If True, will save the returned data such that the same result is
|
| 1074 |
+
returned despite of the state of the underlying file object. This
|
| 1075 |
+
is useful if you want the ``.data`` property to continue working
|
| 1076 |
+
after having ``.read()`` the file object. (Overridden if ``amt`` is
|
| 1077 |
+
set.)
|
| 1078 |
+
"""
|
| 1079 |
+
self._init_decoder()
|
| 1080 |
+
if decode_content is None:
|
| 1081 |
+
decode_content = self.decode_content
|
| 1082 |
+
|
| 1083 |
+
if amt and amt < 0:
|
| 1084 |
+
# Negative numbers and `None` should be treated the same.
|
| 1085 |
+
amt = None
|
| 1086 |
+
elif amt is not None:
|
| 1087 |
+
cache_content = False
|
| 1088 |
+
|
| 1089 |
+
if (
|
| 1090 |
+
self._decoder
|
| 1091 |
+
and self._decoder.has_unconsumed_tail
|
| 1092 |
+
and len(self._decoded_buffer) < amt
|
| 1093 |
+
):
|
| 1094 |
+
decoded_data = self._decode(
|
| 1095 |
+
b"",
|
| 1096 |
+
decode_content,
|
| 1097 |
+
flush_decoder=False,
|
| 1098 |
+
max_length=amt - len(self._decoded_buffer),
|
| 1099 |
+
)
|
| 1100 |
+
self._decoded_buffer.put(decoded_data)
|
| 1101 |
+
if len(self._decoded_buffer) >= amt:
|
| 1102 |
+
return self._decoded_buffer.get(amt)
|
| 1103 |
+
|
| 1104 |
+
data = self._raw_read(amt)
|
| 1105 |
+
if not cache_content:
|
| 1106 |
+
self._uncached_read_occurred = True
|
| 1107 |
+
|
| 1108 |
+
flush_decoder = amt is None or (amt != 0 and not data)
|
| 1109 |
+
|
| 1110 |
+
if (
|
| 1111 |
+
not data
|
| 1112 |
+
and len(self._decoded_buffer) == 0
|
| 1113 |
+
and not (self._decoder and self._decoder.has_unconsumed_tail)
|
| 1114 |
+
):
|
| 1115 |
+
return data
|
| 1116 |
+
|
| 1117 |
+
if amt is None:
|
| 1118 |
+
data = self._decode(data, decode_content, flush_decoder)
|
| 1119 |
+
# It's possible that there is buffered decoded data after a
|
| 1120 |
+
# partial read.
|
| 1121 |
+
if decode_content and len(self._decoded_buffer) > 0:
|
| 1122 |
+
self._decoded_buffer.put(data)
|
| 1123 |
+
data = self._decoded_buffer.get_all()
|
| 1124 |
+
|
| 1125 |
+
if cache_content and not self._uncached_read_occurred:
|
| 1126 |
+
self._body = data
|
| 1127 |
+
else:
|
| 1128 |
+
# do not waste memory on buffer when not decoding
|
| 1129 |
+
if not decode_content:
|
| 1130 |
+
if self._has_decoded_content:
|
| 1131 |
+
raise RuntimeError(
|
| 1132 |
+
"Calling read(decode_content=False) is not supported after "
|
| 1133 |
+
"read(decode_content=True) was called."
|
| 1134 |
+
)
|
| 1135 |
+
return data
|
| 1136 |
+
|
| 1137 |
+
decoded_data = self._decode(
|
| 1138 |
+
data,
|
| 1139 |
+
decode_content,
|
| 1140 |
+
flush_decoder,
|
| 1141 |
+
max_length=amt - len(self._decoded_buffer),
|
| 1142 |
+
)
|
| 1143 |
+
self._decoded_buffer.put(decoded_data)
|
| 1144 |
+
|
| 1145 |
+
while len(self._decoded_buffer) < amt and data:
|
| 1146 |
+
# TODO make sure to initially read enough data to get past the headers
|
| 1147 |
+
# For example, the GZ file header takes 10 bytes, we don't want to read
|
| 1148 |
+
# it one byte at a time
|
| 1149 |
+
data = self._raw_read(amt)
|
| 1150 |
+
decoded_data = self._decode(
|
| 1151 |
+
data,
|
| 1152 |
+
decode_content,
|
| 1153 |
+
flush_decoder,
|
| 1154 |
+
max_length=amt - len(self._decoded_buffer),
|
| 1155 |
+
)
|
| 1156 |
+
self._decoded_buffer.put(decoded_data)
|
| 1157 |
+
data = self._decoded_buffer.get(amt)
|
| 1158 |
+
|
| 1159 |
+
return data
|
| 1160 |
+
|
| 1161 |
+
def read1(
|
| 1162 |
+
self,
|
| 1163 |
+
amt: int | None = None,
|
| 1164 |
+
decode_content: bool | None = None,
|
| 1165 |
+
) -> bytes:
|
| 1166 |
+
"""
|
| 1167 |
+
Similar to ``http.client.HTTPResponse.read1`` and documented
|
| 1168 |
+
in :meth:`io.BufferedReader.read1`, but with an additional parameter:
|
| 1169 |
+
``decode_content``.
|
| 1170 |
+
|
| 1171 |
+
:param amt:
|
| 1172 |
+
How much of the content to read.
|
| 1173 |
+
|
| 1174 |
+
:param decode_content:
|
| 1175 |
+
If True, will attempt to decode the body based on the
|
| 1176 |
+
'content-encoding' header.
|
| 1177 |
+
"""
|
| 1178 |
+
if decode_content is None:
|
| 1179 |
+
decode_content = self.decode_content
|
| 1180 |
+
if amt and amt < 0:
|
| 1181 |
+
# Negative numbers and `None` should be treated the same.
|
| 1182 |
+
amt = None
|
| 1183 |
+
# try and respond without going to the network
|
| 1184 |
+
if self._has_decoded_content:
|
| 1185 |
+
if not decode_content:
|
| 1186 |
+
raise RuntimeError(
|
| 1187 |
+
"Calling read1(decode_content=False) is not supported after "
|
| 1188 |
+
"read1(decode_content=True) was called."
|
| 1189 |
+
)
|
| 1190 |
+
if (
|
| 1191 |
+
self._decoder
|
| 1192 |
+
and self._decoder.has_unconsumed_tail
|
| 1193 |
+
and (amt is None or len(self._decoded_buffer) < amt)
|
| 1194 |
+
):
|
| 1195 |
+
decoded_data = self._decode(
|
| 1196 |
+
b"",
|
| 1197 |
+
decode_content,
|
| 1198 |
+
flush_decoder=False,
|
| 1199 |
+
max_length=(
|
| 1200 |
+
amt - len(self._decoded_buffer) if amt is not None else None
|
| 1201 |
+
),
|
| 1202 |
+
)
|
| 1203 |
+
self._decoded_buffer.put(decoded_data)
|
| 1204 |
+
if len(self._decoded_buffer) > 0:
|
| 1205 |
+
if amt is None:
|
| 1206 |
+
return self._decoded_buffer.get_all()
|
| 1207 |
+
return self._decoded_buffer.get(amt)
|
| 1208 |
+
if amt == 0:
|
| 1209 |
+
return b""
|
| 1210 |
+
|
| 1211 |
+
# FIXME, this method's type doesn't say returning None is possible
|
| 1212 |
+
data = self._raw_read(amt, read1=True)
|
| 1213 |
+
self._uncached_read_occurred = True
|
| 1214 |
+
if not decode_content or data is None:
|
| 1215 |
+
return data
|
| 1216 |
+
|
| 1217 |
+
self._init_decoder()
|
| 1218 |
+
while True:
|
| 1219 |
+
flush_decoder = not data
|
| 1220 |
+
decoded_data = self._decode(
|
| 1221 |
+
data, decode_content, flush_decoder, max_length=amt
|
| 1222 |
+
)
|
| 1223 |
+
self._decoded_buffer.put(decoded_data)
|
| 1224 |
+
if decoded_data or flush_decoder:
|
| 1225 |
+
break
|
| 1226 |
+
data = self._raw_read(8192, read1=True)
|
| 1227 |
+
|
| 1228 |
+
if amt is None:
|
| 1229 |
+
return self._decoded_buffer.get_all()
|
| 1230 |
+
return self._decoded_buffer.get(amt)
|
| 1231 |
+
|
| 1232 |
+
def stream(
|
| 1233 |
+
self, amt: int | None = 2**16, decode_content: bool | None = None
|
| 1234 |
+
) -> typing.Generator[bytes]:
|
| 1235 |
+
"""
|
| 1236 |
+
A generator wrapper for the read() method. A call will block until
|
| 1237 |
+
``amt`` bytes have been read from the connection or until the
|
| 1238 |
+
connection is closed.
|
| 1239 |
+
|
| 1240 |
+
:param amt:
|
| 1241 |
+
How much of the content to read. The generator will return up to
|
| 1242 |
+
much data per iteration, but may return less. This is particularly
|
| 1243 |
+
likely when using compressed data. However, the empty string will
|
| 1244 |
+
never be returned.
|
| 1245 |
+
|
| 1246 |
+
:param decode_content:
|
| 1247 |
+
If True, will attempt to decode the body based on the
|
| 1248 |
+
'content-encoding' header.
|
| 1249 |
+
"""
|
| 1250 |
+
if amt == 0:
|
| 1251 |
+
return
|
| 1252 |
+
|
| 1253 |
+
if self.chunked and self.supports_chunked_reads():
|
| 1254 |
+
yield from self.read_chunked(amt, decode_content=decode_content)
|
| 1255 |
+
else:
|
| 1256 |
+
while (
|
| 1257 |
+
not is_fp_closed(self._fp)
|
| 1258 |
+
or len(self._decoded_buffer) > 0
|
| 1259 |
+
or (self._decoder and self._decoder.has_unconsumed_tail)
|
| 1260 |
+
):
|
| 1261 |
+
data = self.read(amt=amt, decode_content=decode_content)
|
| 1262 |
+
|
| 1263 |
+
if data:
|
| 1264 |
+
yield data
|
| 1265 |
+
|
| 1266 |
+
# Overrides from io.IOBase
|
| 1267 |
+
def readable(self) -> bool:
|
| 1268 |
+
return True
|
| 1269 |
+
|
| 1270 |
+
def shutdown(self) -> None:
|
| 1271 |
+
if not self._sock_shutdown:
|
| 1272 |
+
raise ValueError("Cannot shutdown socket as self._sock_shutdown is not set")
|
| 1273 |
+
if self._connection is None:
|
| 1274 |
+
raise RuntimeError(
|
| 1275 |
+
"Cannot shutdown as connection has already been released to the pool"
|
| 1276 |
+
)
|
| 1277 |
+
self._sock_shutdown(socket.SHUT_RD)
|
| 1278 |
+
|
| 1279 |
+
def close(self) -> None:
|
| 1280 |
+
self._sock_shutdown = None
|
| 1281 |
+
|
| 1282 |
+
if not self.closed and self._fp:
|
| 1283 |
+
self._fp.close()
|
| 1284 |
+
|
| 1285 |
+
if self._connection:
|
| 1286 |
+
self._connection.close()
|
| 1287 |
+
|
| 1288 |
+
if not self.auto_close:
|
| 1289 |
+
io.IOBase.close(self)
|
| 1290 |
+
|
| 1291 |
+
@property
|
| 1292 |
+
def closed(self) -> bool:
|
| 1293 |
+
if not self.auto_close:
|
| 1294 |
+
return io.IOBase.closed.__get__(self) # type: ignore[no-any-return]
|
| 1295 |
+
elif self._fp is None:
|
| 1296 |
+
return True
|
| 1297 |
+
elif hasattr(self._fp, "isclosed"):
|
| 1298 |
+
return self._fp.isclosed()
|
| 1299 |
+
elif hasattr(self._fp, "closed"):
|
| 1300 |
+
return self._fp.closed
|
| 1301 |
+
else:
|
| 1302 |
+
return True
|
| 1303 |
+
|
| 1304 |
+
def fileno(self) -> int:
|
| 1305 |
+
if self._fp is None:
|
| 1306 |
+
raise OSError("HTTPResponse has no file to get a fileno from")
|
| 1307 |
+
elif hasattr(self._fp, "fileno"):
|
| 1308 |
+
return self._fp.fileno()
|
| 1309 |
+
else:
|
| 1310 |
+
raise OSError(
|
| 1311 |
+
"The file-like object this HTTPResponse is wrapped "
|
| 1312 |
+
"around has no file descriptor"
|
| 1313 |
+
)
|
| 1314 |
+
|
| 1315 |
+
def flush(self) -> None:
|
| 1316 |
+
if (
|
| 1317 |
+
self._fp is not None
|
| 1318 |
+
and hasattr(self._fp, "flush")
|
| 1319 |
+
and not getattr(self._fp, "closed", False)
|
| 1320 |
+
):
|
| 1321 |
+
return self._fp.flush()
|
| 1322 |
+
|
| 1323 |
+
def supports_chunked_reads(self) -> bool:
|
| 1324 |
+
"""
|
| 1325 |
+
Checks if the underlying file-like object looks like a
|
| 1326 |
+
:class:`http.client.HTTPResponse` object. We do this by testing for
|
| 1327 |
+
the fp attribute. If it is present we assume it returns raw chunks as
|
| 1328 |
+
processed by read_chunked().
|
| 1329 |
+
"""
|
| 1330 |
+
return hasattr(self._fp, "fp")
|
| 1331 |
+
|
| 1332 |
+
def _update_chunk_length(self) -> None:
|
| 1333 |
+
# First, we'll figure out length of a chunk and then
|
| 1334 |
+
# we'll try to read it from socket.
|
| 1335 |
+
if self.chunk_left is not None:
|
| 1336 |
+
return None
|
| 1337 |
+
line = self._fp.fp.readline() # type: ignore[union-attr]
|
| 1338 |
+
line = line.split(b";", 1)[0]
|
| 1339 |
+
try:
|
| 1340 |
+
self.chunk_left = int(line, 16)
|
| 1341 |
+
except ValueError:
|
| 1342 |
+
self.close()
|
| 1343 |
+
if line:
|
| 1344 |
+
# Invalid chunked protocol response, abort.
|
| 1345 |
+
raise InvalidChunkLength(self, line) from None
|
| 1346 |
+
else:
|
| 1347 |
+
# Truncated at start of next chunk
|
| 1348 |
+
raise ProtocolError("Response ended prematurely") from None
|
| 1349 |
+
|
| 1350 |
+
def _handle_chunk(self, amt: int | None) -> bytes:
|
| 1351 |
+
returned_chunk = None
|
| 1352 |
+
if amt is None:
|
| 1353 |
+
chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr]
|
| 1354 |
+
returned_chunk = chunk
|
| 1355 |
+
self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk.
|
| 1356 |
+
self.chunk_left = None
|
| 1357 |
+
elif self.chunk_left is not None and amt < self.chunk_left:
|
| 1358 |
+
value = self._fp._safe_read(amt) # type: ignore[union-attr]
|
| 1359 |
+
self.chunk_left = self.chunk_left - amt
|
| 1360 |
+
returned_chunk = value
|
| 1361 |
+
elif amt == self.chunk_left:
|
| 1362 |
+
value = self._fp._safe_read(amt) # type: ignore[union-attr]
|
| 1363 |
+
self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk.
|
| 1364 |
+
self.chunk_left = None
|
| 1365 |
+
returned_chunk = value
|
| 1366 |
+
else: # amt > self.chunk_left
|
| 1367 |
+
returned_chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr]
|
| 1368 |
+
self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk.
|
| 1369 |
+
self.chunk_left = None
|
| 1370 |
+
return returned_chunk # type: ignore[no-any-return]
|
| 1371 |
+
|
| 1372 |
+
def read_chunked(
|
| 1373 |
+
self, amt: int | None = None, decode_content: bool | None = None
|
| 1374 |
+
) -> typing.Generator[bytes]:
|
| 1375 |
+
"""
|
| 1376 |
+
Similar to :meth:`HTTPResponse.read`, but with an additional
|
| 1377 |
+
parameter: ``decode_content``.
|
| 1378 |
+
|
| 1379 |
+
:param amt:
|
| 1380 |
+
How much of the content to read. If specified, caching is skipped
|
| 1381 |
+
because it doesn't make sense to cache partial content as the full
|
| 1382 |
+
response.
|
| 1383 |
+
|
| 1384 |
+
:param decode_content:
|
| 1385 |
+
If True, will attempt to decode the body based on the
|
| 1386 |
+
'content-encoding' header.
|
| 1387 |
+
"""
|
| 1388 |
+
self._init_decoder()
|
| 1389 |
+
# FIXME: Rewrite this method and make it a class with a better structured logic.
|
| 1390 |
+
if not self.chunked:
|
| 1391 |
+
raise ResponseNotChunked(
|
| 1392 |
+
"Response is not chunked. "
|
| 1393 |
+
"Header 'transfer-encoding: chunked' is missing."
|
| 1394 |
+
)
|
| 1395 |
+
if not self.supports_chunked_reads():
|
| 1396 |
+
raise BodyNotHttplibCompatible(
|
| 1397 |
+
"Body should be http.client.HTTPResponse like. "
|
| 1398 |
+
"It should have have an fp attribute which returns raw chunks."
|
| 1399 |
+
)
|
| 1400 |
+
|
| 1401 |
+
with self._error_catcher():
|
| 1402 |
+
# Don't bother reading the body of a HEAD request.
|
| 1403 |
+
if self._original_response and is_response_to_head(self._original_response):
|
| 1404 |
+
self._original_response.close()
|
| 1405 |
+
return None
|
| 1406 |
+
|
| 1407 |
+
# If a response is already read and closed
|
| 1408 |
+
# then return immediately.
|
| 1409 |
+
if self._fp.fp is None: # type: ignore[union-attr]
|
| 1410 |
+
return None
|
| 1411 |
+
|
| 1412 |
+
if amt == 0:
|
| 1413 |
+
return
|
| 1414 |
+
elif amt and amt < 0:
|
| 1415 |
+
# Negative numbers and `None` should be treated the same,
|
| 1416 |
+
# but httplib handles only `None` correctly.
|
| 1417 |
+
amt = None
|
| 1418 |
+
|
| 1419 |
+
while True:
|
| 1420 |
+
# First, check if any data is left in the decoder's buffer.
|
| 1421 |
+
if self._decoder and self._decoder.has_unconsumed_tail:
|
| 1422 |
+
chunk = b""
|
| 1423 |
+
else:
|
| 1424 |
+
self._update_chunk_length()
|
| 1425 |
+
self._uncached_read_occurred = True
|
| 1426 |
+
if self.chunk_left == 0:
|
| 1427 |
+
break
|
| 1428 |
+
chunk = self._handle_chunk(amt)
|
| 1429 |
+
decoded = self._decode(
|
| 1430 |
+
chunk,
|
| 1431 |
+
decode_content=decode_content,
|
| 1432 |
+
flush_decoder=False,
|
| 1433 |
+
max_length=amt,
|
| 1434 |
+
)
|
| 1435 |
+
if decoded:
|
| 1436 |
+
yield decoded
|
| 1437 |
+
|
| 1438 |
+
if decode_content:
|
| 1439 |
+
# On CPython and PyPy, we should never need to flush the
|
| 1440 |
+
# decoder. However, on Jython we *might* need to, so
|
| 1441 |
+
# lets defensively do it anyway.
|
| 1442 |
+
decoded = self._flush_decoder()
|
| 1443 |
+
if decoded: # Platform-specific: Jython.
|
| 1444 |
+
yield decoded
|
| 1445 |
+
|
| 1446 |
+
# Chunk content ends with \r\n: discard it.
|
| 1447 |
+
while self._fp is not None:
|
| 1448 |
+
line = self._fp.fp.readline()
|
| 1449 |
+
if not line:
|
| 1450 |
+
# Some sites may not end with '\r\n'.
|
| 1451 |
+
break
|
| 1452 |
+
if line == b"\r\n":
|
| 1453 |
+
break
|
| 1454 |
+
|
| 1455 |
+
# We read everything; close the "file".
|
| 1456 |
+
if self._original_response:
|
| 1457 |
+
self._original_response.close()
|
| 1458 |
+
|
| 1459 |
+
@property
|
| 1460 |
+
def url(self) -> str | None:
|
| 1461 |
+
"""
|
| 1462 |
+
Returns the URL that was the source of this response.
|
| 1463 |
+
If the request that generated this response redirected, this method
|
| 1464 |
+
will return the final redirect location.
|
| 1465 |
+
"""
|
| 1466 |
+
return self._request_url
|
| 1467 |
+
|
| 1468 |
+
@url.setter
|
| 1469 |
+
def url(self, url: str | None) -> None:
|
| 1470 |
+
self._request_url = url
|
| 1471 |
+
|
| 1472 |
+
def __iter__(self) -> typing.Iterator[bytes]:
|
| 1473 |
+
buffer: list[bytes] = []
|
| 1474 |
+
for chunk in self.stream(decode_content=True):
|
| 1475 |
+
if b"\n" in chunk:
|
| 1476 |
+
chunks = chunk.split(b"\n")
|
| 1477 |
+
yield b"".join(buffer) + chunks[0] + b"\n"
|
| 1478 |
+
for x in chunks[1:-1]:
|
| 1479 |
+
yield x + b"\n"
|
| 1480 |
+
if chunks[-1]:
|
| 1481 |
+
buffer = [chunks[-1]]
|
| 1482 |
+
else:
|
| 1483 |
+
buffer = []
|
| 1484 |
+
else:
|
| 1485 |
+
buffer.append(chunk)
|
| 1486 |
+
if buffer:
|
| 1487 |
+
yield b"".join(buffer)
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/__init__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# For backwards compatibility, provide imports that used to be here.
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
from .connection import is_connection_dropped
|
| 5 |
+
from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers
|
| 6 |
+
from .response import is_fp_closed
|
| 7 |
+
from .retry import Retry
|
| 8 |
+
from .ssl_ import (
|
| 9 |
+
ALPN_PROTOCOLS,
|
| 10 |
+
IS_PYOPENSSL,
|
| 11 |
+
SSLContext,
|
| 12 |
+
assert_fingerprint,
|
| 13 |
+
create_urllib3_context,
|
| 14 |
+
resolve_cert_reqs,
|
| 15 |
+
resolve_ssl_version,
|
| 16 |
+
ssl_wrap_socket,
|
| 17 |
+
)
|
| 18 |
+
from .timeout import Timeout
|
| 19 |
+
from .url import Url, parse_url
|
| 20 |
+
from .wait import wait_for_read, wait_for_write
|
| 21 |
+
|
| 22 |
+
__all__ = (
|
| 23 |
+
"IS_PYOPENSSL",
|
| 24 |
+
"SSLContext",
|
| 25 |
+
"ALPN_PROTOCOLS",
|
| 26 |
+
"Retry",
|
| 27 |
+
"Timeout",
|
| 28 |
+
"Url",
|
| 29 |
+
"assert_fingerprint",
|
| 30 |
+
"create_urllib3_context",
|
| 31 |
+
"is_connection_dropped",
|
| 32 |
+
"is_fp_closed",
|
| 33 |
+
"parse_url",
|
| 34 |
+
"make_headers",
|
| 35 |
+
"resolve_cert_reqs",
|
| 36 |
+
"resolve_ssl_version",
|
| 37 |
+
"ssl_wrap_socket",
|
| 38 |
+
"wait_for_read",
|
| 39 |
+
"wait_for_write",
|
| 40 |
+
"SKIP_HEADER",
|
| 41 |
+
"SKIPPABLE_HEADERS",
|
| 42 |
+
)
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/connection.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import socket
|
| 4 |
+
import typing
|
| 5 |
+
|
| 6 |
+
from ..exceptions import LocationParseError
|
| 7 |
+
from .timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT
|
| 8 |
+
|
| 9 |
+
_TYPE_SOCKET_OPTIONS = list[tuple[int, int, typing.Union[int, bytes]]]
|
| 10 |
+
|
| 11 |
+
if typing.TYPE_CHECKING:
|
| 12 |
+
from .._base_connection import BaseHTTPConnection
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def is_connection_dropped(conn: BaseHTTPConnection) -> bool: # Platform-specific
|
| 16 |
+
"""
|
| 17 |
+
Returns True if the connection is dropped and should be closed.
|
| 18 |
+
:param conn: :class:`urllib3.connection.HTTPConnection` object.
|
| 19 |
+
"""
|
| 20 |
+
return not conn.is_connected
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# This function is copied from socket.py in the Python 2.7 standard
|
| 24 |
+
# library test suite. Added to its signature is only `socket_options`.
|
| 25 |
+
# One additional modification is that we avoid binding to IPv6 servers
|
| 26 |
+
# discovered in DNS if the system doesn't have IPv6 functionality.
|
| 27 |
+
def create_connection(
|
| 28 |
+
address: tuple[str, int],
|
| 29 |
+
timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,
|
| 30 |
+
source_address: tuple[str, int] | None = None,
|
| 31 |
+
socket_options: _TYPE_SOCKET_OPTIONS | None = None,
|
| 32 |
+
) -> socket.socket:
|
| 33 |
+
"""Connect to *address* and return the socket object.
|
| 34 |
+
|
| 35 |
+
Convenience function. Connect to *address* (a 2-tuple ``(host,
|
| 36 |
+
port)``) and return the socket object. Passing the optional
|
| 37 |
+
*timeout* parameter will set the timeout on the socket instance
|
| 38 |
+
before attempting to connect. If no *timeout* is supplied, the
|
| 39 |
+
global default timeout setting returned by :func:`socket.getdefaulttimeout`
|
| 40 |
+
is used. If *source_address* is set it must be a tuple of (host, port)
|
| 41 |
+
for the socket to bind as a source address before making the connection.
|
| 42 |
+
An host of '' or port 0 tells the OS to use the default.
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
host, port = address
|
| 46 |
+
if host.startswith("["):
|
| 47 |
+
host = host.strip("[]")
|
| 48 |
+
err = None
|
| 49 |
+
|
| 50 |
+
# Using the value from allowed_gai_family() in the context of getaddrinfo lets
|
| 51 |
+
# us select whether to work with IPv4 DNS records, IPv6 records, or both.
|
| 52 |
+
# The original create_connection function always returns all records.
|
| 53 |
+
family = allowed_gai_family()
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
host.encode("idna")
|
| 57 |
+
except UnicodeError:
|
| 58 |
+
raise LocationParseError(f"'{host}', label empty or too long") from None
|
| 59 |
+
|
| 60 |
+
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
|
| 61 |
+
af, socktype, proto, canonname, sa = res
|
| 62 |
+
sock = None
|
| 63 |
+
try:
|
| 64 |
+
sock = socket.socket(af, socktype, proto)
|
| 65 |
+
|
| 66 |
+
# If provided, set socket level options before connecting.
|
| 67 |
+
_set_socket_options(sock, socket_options)
|
| 68 |
+
|
| 69 |
+
if timeout is not _DEFAULT_TIMEOUT:
|
| 70 |
+
sock.settimeout(timeout)
|
| 71 |
+
if source_address:
|
| 72 |
+
sock.bind(source_address)
|
| 73 |
+
sock.connect(sa)
|
| 74 |
+
# Break explicitly a reference cycle
|
| 75 |
+
err = None
|
| 76 |
+
return sock
|
| 77 |
+
|
| 78 |
+
except OSError as _:
|
| 79 |
+
err = _
|
| 80 |
+
if sock is not None:
|
| 81 |
+
sock.close()
|
| 82 |
+
|
| 83 |
+
if err is not None:
|
| 84 |
+
try:
|
| 85 |
+
raise err
|
| 86 |
+
finally:
|
| 87 |
+
# Break explicitly a reference cycle
|
| 88 |
+
err = None
|
| 89 |
+
else:
|
| 90 |
+
raise OSError("getaddrinfo returns an empty list")
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _set_socket_options(
|
| 94 |
+
sock: socket.socket, options: _TYPE_SOCKET_OPTIONS | None
|
| 95 |
+
) -> None:
|
| 96 |
+
if options is None:
|
| 97 |
+
return
|
| 98 |
+
|
| 99 |
+
for opt in options:
|
| 100 |
+
sock.setsockopt(*opt)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def allowed_gai_family() -> socket.AddressFamily:
|
| 104 |
+
"""This function is designed to work in the context of
|
| 105 |
+
getaddrinfo, where family=socket.AF_UNSPEC is the default and
|
| 106 |
+
will perform a DNS search for both IPv6 and IPv4 records."""
|
| 107 |
+
|
| 108 |
+
family = socket.AF_INET
|
| 109 |
+
if HAS_IPV6:
|
| 110 |
+
family = socket.AF_UNSPEC
|
| 111 |
+
return family
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _has_ipv6(host: str) -> bool:
|
| 115 |
+
"""Returns True if the system can bind an IPv6 address."""
|
| 116 |
+
sock = None
|
| 117 |
+
has_ipv6 = False
|
| 118 |
+
|
| 119 |
+
if socket.has_ipv6:
|
| 120 |
+
# has_ipv6 returns true if cPython was compiled with IPv6 support.
|
| 121 |
+
# It does not tell us if the system has IPv6 support enabled. To
|
| 122 |
+
# determine that we must bind to an IPv6 address.
|
| 123 |
+
# https://github.com/urllib3/urllib3/pull/611
|
| 124 |
+
# https://bugs.python.org/issue658327
|
| 125 |
+
try:
|
| 126 |
+
sock = socket.socket(socket.AF_INET6)
|
| 127 |
+
sock.bind((host, 0))
|
| 128 |
+
has_ipv6 = True
|
| 129 |
+
except Exception:
|
| 130 |
+
pass
|
| 131 |
+
|
| 132 |
+
if sock:
|
| 133 |
+
sock.close()
|
| 134 |
+
return has_ipv6
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
HAS_IPV6 = _has_ipv6("::1")
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/proxy.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import typing
|
| 4 |
+
|
| 5 |
+
from .url import Url
|
| 6 |
+
|
| 7 |
+
if typing.TYPE_CHECKING:
|
| 8 |
+
from ..connection import ProxyConfig
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def connection_requires_http_tunnel(
|
| 12 |
+
proxy_url: Url | None = None,
|
| 13 |
+
proxy_config: ProxyConfig | None = None,
|
| 14 |
+
destination_scheme: str | None = None,
|
| 15 |
+
) -> bool:
|
| 16 |
+
"""
|
| 17 |
+
Returns True if the connection requires an HTTP CONNECT through the proxy.
|
| 18 |
+
|
| 19 |
+
:param URL proxy_url:
|
| 20 |
+
URL of the proxy.
|
| 21 |
+
:param ProxyConfig proxy_config:
|
| 22 |
+
Proxy configuration from poolmanager.py
|
| 23 |
+
:param str destination_scheme:
|
| 24 |
+
The scheme of the destination. (i.e https, http, etc)
|
| 25 |
+
"""
|
| 26 |
+
# If we're not using a proxy, no way to use a tunnel.
|
| 27 |
+
if proxy_url is None:
|
| 28 |
+
return False
|
| 29 |
+
|
| 30 |
+
# HTTP destinations never require tunneling, we always forward.
|
| 31 |
+
if destination_scheme == "http":
|
| 32 |
+
return False
|
| 33 |
+
|
| 34 |
+
# Support for forwarding with HTTPS proxies and HTTPS destinations.
|
| 35 |
+
if (
|
| 36 |
+
proxy_url.scheme == "https"
|
| 37 |
+
and proxy_config
|
| 38 |
+
and proxy_config.use_forwarding_for_https
|
| 39 |
+
):
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
# Otherwise always use a tunnel.
|
| 43 |
+
return True
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/request.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import io
|
| 4 |
+
import sys
|
| 5 |
+
import typing
|
| 6 |
+
from base64 import b64encode
|
| 7 |
+
from enum import Enum
|
| 8 |
+
|
| 9 |
+
from ..exceptions import UnrewindableBodyError
|
| 10 |
+
from .util import to_bytes
|
| 11 |
+
|
| 12 |
+
if typing.TYPE_CHECKING:
|
| 13 |
+
from typing import Final
|
| 14 |
+
|
| 15 |
+
# Pass as a value within ``headers`` to skip
|
| 16 |
+
# emitting some HTTP headers that are added automatically.
|
| 17 |
+
# The only headers that are supported are ``Accept-Encoding``,
|
| 18 |
+
# ``Host``, and ``User-Agent``.
|
| 19 |
+
SKIP_HEADER = "@@@SKIP_HEADER@@@"
|
| 20 |
+
SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"])
|
| 21 |
+
|
| 22 |
+
ACCEPT_ENCODING = "gzip,deflate"
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
if sys.version_info >= (3, 14):
|
| 26 |
+
from compression import zstd as _unused_module_zstd # noqa: F401
|
| 27 |
+
else:
|
| 28 |
+
from backports import zstd as _unused_module_zstd # noqa: F401
|
| 29 |
+
except ImportError:
|
| 30 |
+
pass
|
| 31 |
+
else:
|
| 32 |
+
ACCEPT_ENCODING += ",zstd"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class _TYPE_FAILEDTELL(Enum):
|
| 36 |
+
token = 0
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
_FAILEDTELL: Final[_TYPE_FAILEDTELL] = _TYPE_FAILEDTELL.token
|
| 40 |
+
|
| 41 |
+
_TYPE_BODY_POSITION = typing.Union[int, _TYPE_FAILEDTELL]
|
| 42 |
+
|
| 43 |
+
# When sending a request with these methods we aren't expecting
|
| 44 |
+
# a body so don't need to set an explicit 'Content-Length: 0'
|
| 45 |
+
# The reason we do this in the negative instead of tracking methods
|
| 46 |
+
# which 'should' have a body is because unknown methods should be
|
| 47 |
+
# treated as if they were 'POST' which *does* expect a body.
|
| 48 |
+
_METHODS_NOT_EXPECTING_BODY = {"GET", "HEAD", "DELETE", "TRACE", "OPTIONS", "CONNECT"}
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def make_headers(
|
| 52 |
+
keep_alive: bool | None = None,
|
| 53 |
+
accept_encoding: bool | list[str] | str | None = None,
|
| 54 |
+
user_agent: str | None = None,
|
| 55 |
+
basic_auth: str | None = None,
|
| 56 |
+
proxy_basic_auth: str | None = None,
|
| 57 |
+
disable_cache: bool | None = None,
|
| 58 |
+
) -> dict[str, str]:
|
| 59 |
+
"""
|
| 60 |
+
Shortcuts for generating request headers.
|
| 61 |
+
|
| 62 |
+
:param keep_alive:
|
| 63 |
+
If ``True``, adds 'connection: keep-alive' header.
|
| 64 |
+
|
| 65 |
+
:param accept_encoding:
|
| 66 |
+
Can be a boolean, list, or string.
|
| 67 |
+
``True`` translates to 'gzip,deflate'. If the dependencies for
|
| 68 |
+
Brotli (either the ``brotli`` or ``brotlicffi`` package) and/or
|
| 69 |
+
Zstandard (the ``backports.zstd`` package for Python before 3.14)
|
| 70 |
+
algorithms are installed, then their encodings are
|
| 71 |
+
included in the string ('br' and 'zstd', respectively).
|
| 72 |
+
List will get joined by comma.
|
| 73 |
+
String will be used as provided.
|
| 74 |
+
|
| 75 |
+
:param user_agent:
|
| 76 |
+
String representing the user-agent you want, such as
|
| 77 |
+
"python-urllib3/0.6"
|
| 78 |
+
|
| 79 |
+
:param basic_auth:
|
| 80 |
+
Colon-separated username:password string for 'authorization: basic ...'
|
| 81 |
+
auth header.
|
| 82 |
+
|
| 83 |
+
:param proxy_basic_auth:
|
| 84 |
+
Colon-separated username:password string for 'proxy-authorization: basic ...'
|
| 85 |
+
auth header.
|
| 86 |
+
|
| 87 |
+
:param disable_cache:
|
| 88 |
+
If ``True``, adds 'cache-control: no-cache' header.
|
| 89 |
+
|
| 90 |
+
Example:
|
| 91 |
+
|
| 92 |
+
.. code-block:: python
|
| 93 |
+
|
| 94 |
+
from pip._vendor import urllib3
|
| 95 |
+
|
| 96 |
+
print(urllib3.util.make_headers(keep_alive=True, user_agent="Batman/1.0"))
|
| 97 |
+
# {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'}
|
| 98 |
+
print(urllib3.util.make_headers(accept_encoding=True))
|
| 99 |
+
# {'accept-encoding': 'gzip,deflate'}
|
| 100 |
+
"""
|
| 101 |
+
headers: dict[str, str] = {}
|
| 102 |
+
if accept_encoding:
|
| 103 |
+
if isinstance(accept_encoding, str):
|
| 104 |
+
pass
|
| 105 |
+
elif isinstance(accept_encoding, list):
|
| 106 |
+
accept_encoding = ",".join(accept_encoding)
|
| 107 |
+
else:
|
| 108 |
+
accept_encoding = ACCEPT_ENCODING
|
| 109 |
+
headers["accept-encoding"] = accept_encoding
|
| 110 |
+
|
| 111 |
+
if user_agent:
|
| 112 |
+
headers["user-agent"] = user_agent
|
| 113 |
+
|
| 114 |
+
if keep_alive:
|
| 115 |
+
headers["connection"] = "keep-alive"
|
| 116 |
+
|
| 117 |
+
if basic_auth:
|
| 118 |
+
headers["authorization"] = (
|
| 119 |
+
f"Basic {b64encode(basic_auth.encode('latin-1')).decode()}"
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
if proxy_basic_auth:
|
| 123 |
+
headers["proxy-authorization"] = (
|
| 124 |
+
f"Basic {b64encode(proxy_basic_auth.encode('latin-1')).decode()}"
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
if disable_cache:
|
| 128 |
+
headers["cache-control"] = "no-cache"
|
| 129 |
+
|
| 130 |
+
return headers
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def set_file_position(
|
| 134 |
+
body: typing.Any, pos: _TYPE_BODY_POSITION | None
|
| 135 |
+
) -> _TYPE_BODY_POSITION | None:
|
| 136 |
+
"""
|
| 137 |
+
If a position is provided, move file to that point.
|
| 138 |
+
Otherwise, we'll attempt to record a position for future use.
|
| 139 |
+
"""
|
| 140 |
+
if pos is not None:
|
| 141 |
+
rewind_body(body, pos)
|
| 142 |
+
elif getattr(body, "tell", None) is not None:
|
| 143 |
+
try:
|
| 144 |
+
pos = body.tell()
|
| 145 |
+
except OSError:
|
| 146 |
+
# This differentiates from None, allowing us to catch
|
| 147 |
+
# a failed `tell()` later when trying to rewind the body.
|
| 148 |
+
pos = _FAILEDTELL
|
| 149 |
+
|
| 150 |
+
return pos
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def rewind_body(body: typing.IO[typing.AnyStr], body_pos: _TYPE_BODY_POSITION) -> None:
|
| 154 |
+
"""
|
| 155 |
+
Attempt to rewind body to a certain position.
|
| 156 |
+
Primarily used for request redirects and retries.
|
| 157 |
+
|
| 158 |
+
:param body:
|
| 159 |
+
File-like object that supports seek.
|
| 160 |
+
|
| 161 |
+
:param int pos:
|
| 162 |
+
Position to seek to in file.
|
| 163 |
+
"""
|
| 164 |
+
body_seek = getattr(body, "seek", None)
|
| 165 |
+
if body_seek is not None and isinstance(body_pos, int):
|
| 166 |
+
try:
|
| 167 |
+
body_seek(body_pos)
|
| 168 |
+
except OSError as e:
|
| 169 |
+
raise UnrewindableBodyError(
|
| 170 |
+
"An error occurred when rewinding request body for redirect/retry."
|
| 171 |
+
) from e
|
| 172 |
+
elif body_pos is _FAILEDTELL:
|
| 173 |
+
raise UnrewindableBodyError(
|
| 174 |
+
"Unable to record file position for rewinding "
|
| 175 |
+
"request body during a redirect/retry."
|
| 176 |
+
)
|
| 177 |
+
else:
|
| 178 |
+
raise ValueError(
|
| 179 |
+
f"body_pos must be of type integer, instead it was {type(body_pos)}."
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
class ChunksAndContentLength(typing.NamedTuple):
|
| 184 |
+
chunks: typing.Iterable[bytes] | None
|
| 185 |
+
content_length: int | None
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def body_to_chunks(
|
| 189 |
+
body: typing.Any | None, method: str, blocksize: int
|
| 190 |
+
) -> ChunksAndContentLength:
|
| 191 |
+
"""Takes the HTTP request method, body, and blocksize and
|
| 192 |
+
transforms them into an iterable of chunks to pass to
|
| 193 |
+
socket.sendall() and an optional 'Content-Length' header.
|
| 194 |
+
|
| 195 |
+
A 'Content-Length' of 'None' indicates the length of the body
|
| 196 |
+
can't be determined so should use 'Transfer-Encoding: chunked'
|
| 197 |
+
for framing instead.
|
| 198 |
+
"""
|
| 199 |
+
|
| 200 |
+
chunks: typing.Iterable[bytes] | None
|
| 201 |
+
content_length: int | None
|
| 202 |
+
|
| 203 |
+
# No body, we need to make a recommendation on 'Content-Length'
|
| 204 |
+
# based on whether that request method is expected to have
|
| 205 |
+
# a body or not.
|
| 206 |
+
if body is None:
|
| 207 |
+
chunks = None
|
| 208 |
+
if method.upper() not in _METHODS_NOT_EXPECTING_BODY:
|
| 209 |
+
content_length = 0
|
| 210 |
+
else:
|
| 211 |
+
content_length = None
|
| 212 |
+
|
| 213 |
+
# Bytes or strings become bytes
|
| 214 |
+
elif isinstance(body, (str, bytes)):
|
| 215 |
+
chunks = (to_bytes(body),)
|
| 216 |
+
content_length = len(chunks[0])
|
| 217 |
+
|
| 218 |
+
# File-like object, TODO: use seek() and tell() for length?
|
| 219 |
+
elif hasattr(body, "read"):
|
| 220 |
+
|
| 221 |
+
def chunk_readable() -> typing.Iterable[bytes]:
|
| 222 |
+
encode = isinstance(body, io.TextIOBase)
|
| 223 |
+
while True:
|
| 224 |
+
datablock = body.read(blocksize)
|
| 225 |
+
if not datablock:
|
| 226 |
+
break
|
| 227 |
+
if encode:
|
| 228 |
+
datablock = datablock.encode("utf-8")
|
| 229 |
+
yield datablock
|
| 230 |
+
|
| 231 |
+
chunks = chunk_readable()
|
| 232 |
+
content_length = None
|
| 233 |
+
|
| 234 |
+
# Otherwise we need to start checking via duck-typing.
|
| 235 |
+
else:
|
| 236 |
+
try:
|
| 237 |
+
# Check if the body implements the buffer API.
|
| 238 |
+
mv = memoryview(body)
|
| 239 |
+
except TypeError:
|
| 240 |
+
try:
|
| 241 |
+
# Check if the body is an iterable
|
| 242 |
+
chunks = iter(body)
|
| 243 |
+
content_length = None
|
| 244 |
+
except TypeError:
|
| 245 |
+
raise TypeError(
|
| 246 |
+
f"'body' must be a bytes-like object, file-like "
|
| 247 |
+
f"object, or iterable. Instead was {body!r}"
|
| 248 |
+
) from None
|
| 249 |
+
else:
|
| 250 |
+
# Since it implements the buffer API can be passed directly to socket.sendall()
|
| 251 |
+
chunks = (body,)
|
| 252 |
+
content_length = mv.nbytes
|
| 253 |
+
|
| 254 |
+
return ChunksAndContentLength(chunks=chunks, content_length=content_length)
|
.venv/lib/python3.14/site-packages/pip/_vendor/urllib3/util/response.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import http.client as httplib
|
| 4 |
+
from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect
|
| 5 |
+
|
| 6 |
+
from ..exceptions import HeaderParsingError
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def is_fp_closed(obj: object) -> bool:
|
| 10 |
+
"""
|
| 11 |
+
Checks whether a given file-like object is closed.
|
| 12 |
+
|
| 13 |
+
:param obj:
|
| 14 |
+
The file-like object to check.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
# Check `isclosed()` first, in case Python3 doesn't set `closed`.
|
| 19 |
+
# GH Issue #928
|
| 20 |
+
return obj.isclosed() # type: ignore[no-any-return, attr-defined]
|
| 21 |
+
except AttributeError:
|
| 22 |
+
pass
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
# Check via the official file-like-object way.
|
| 26 |
+
return obj.closed # type: ignore[no-any-return, attr-defined]
|
| 27 |
+
except AttributeError:
|
| 28 |
+
pass
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
# Check if the object is a container for another file-like object that
|
| 32 |
+
# gets released on exhaustion (e.g. HTTPResponse).
|
| 33 |
+
return obj.fp is None # type: ignore[attr-defined]
|
| 34 |
+
except AttributeError:
|
| 35 |
+
pass
|
| 36 |
+
|
| 37 |
+
raise ValueError("Unable to determine whether fp is closed.")
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def assert_header_parsing(headers: httplib.HTTPMessage) -> None:
|
| 41 |
+
"""
|
| 42 |
+
Asserts whether all headers have been successfully parsed.
|
| 43 |
+
Extracts encountered errors from the result of parsing headers.
|
| 44 |
+
|
| 45 |
+
Only works on Python 3.
|
| 46 |
+
|
| 47 |
+
:param http.client.HTTPMessage headers: Headers to verify.
|
| 48 |
+
|
| 49 |
+
:raises urllib3.exceptions.HeaderParsingError:
|
| 50 |
+
If parsing errors are found.
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
# This will fail silently if we pass in the wrong kind of parameter.
|
| 54 |
+
# To make debugging easier add an explicit check.
|
| 55 |
+
if not isinstance(headers, httplib.HTTPMessage):
|
| 56 |
+
raise TypeError(f"expected httplib.Message, got {type(headers)}.")
|
| 57 |
+
|
| 58 |
+
unparsed_data = None
|
| 59 |
+
|
| 60 |
+
# get_payload is actually email.message.Message.get_payload;
|
| 61 |
+
# we're only interested in the result if it's not a multipart message
|
| 62 |
+
if not headers.is_multipart():
|
| 63 |
+
payload = headers.get_payload()
|
| 64 |
+
|
| 65 |
+
if isinstance(payload, (bytes, str)):
|
| 66 |
+
unparsed_data = payload
|
| 67 |
+
|
| 68 |
+
# httplib is assuming a response body is available
|
| 69 |
+
# when parsing headers even when httplib only sends
|
| 70 |
+
# header data to parse_headers() This results in
|
| 71 |
+
# defects on multipart responses in particular.
|
| 72 |
+
# See: https://github.com/urllib3/urllib3/issues/800
|
| 73 |
+
|
| 74 |
+
# So we ignore the following defects:
|
| 75 |
+
# - StartBoundaryNotFoundDefect:
|
| 76 |
+
# The claimed start boundary was never found.
|
| 77 |
+
# - MultipartInvariantViolationDefect:
|
| 78 |
+
# A message claimed to be a multipart but no subparts were found.
|
| 79 |
+
defects = [
|
| 80 |
+
defect
|
| 81 |
+
for defect in headers.defects
|
| 82 |
+
if not isinstance(
|
| 83 |
+
defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect)
|
| 84 |
+
)
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
if defects or unparsed_data:
|
| 88 |
+
raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def is_response_to_head(response: httplib.HTTPResponse) -> bool:
|
| 92 |
+
"""
|
| 93 |
+
Checks whether the request of a response has been a HEAD-request.
|
| 94 |
+
|
| 95 |
+
:param http.client.HTTPResponse response:
|
| 96 |
+
Response to check if the originating request
|
| 97 |
+
used 'HEAD' as a method.
|
| 98 |
+
"""
|
| 99 |
+
# FIXME: Can we do this somehow without accessing private httplib _method?
|
| 100 |
+
method_str = response._method # type: str # type: ignore[attr-defined]
|
| 101 |
+
return method_str.upper() == "HEAD"
|