This is page 97 of 168. Use http://codebase.md/romanshablio/mcp_server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .DS_Store
├── .venv
│ ├── __pycache__
│ │ └── hello.cpython-312.pyc
│ ├── bin
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── Activate.ps1
│ │ ├── flask
│ │ ├── normalizer
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.12
│ │ ├── python
│ │ ├── python3
│ │ └── python3.12
│ ├── hello.py
│ ├── lib
│ │ └── python3.12
│ │ └── site-packages
│ │ ├── beautifulsoup4-4.12.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ ├── AUTHORS
│ │ │ │ └── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── blinker
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _utilities.cpython-312.pyc
│ │ │ │ └── base.cpython-312.pyc
│ │ │ ├── _utilities.py
│ │ │ ├── base.py
│ │ │ └── py.typed
│ │ ├── blinker-1.8.2.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── bs4
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── css.cpython-312.pyc
│ │ │ │ ├── dammit.cpython-312.pyc
│ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ ├── element.cpython-312.pyc
│ │ │ │ └── formatter.cpython-312.pyc
│ │ │ ├── builder
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _html5lib.cpython-312.pyc
│ │ │ │ │ ├── _htmlparser.cpython-312.pyc
│ │ │ │ │ └── _lxml.cpython-312.pyc
│ │ │ │ ├── _html5lib.py
│ │ │ │ ├── _htmlparser.py
│ │ │ │ └── _lxml.py
│ │ │ ├── css.py
│ │ │ ├── dammit.py
│ │ │ ├── diagnose.py
│ │ │ ├── element.py
│ │ │ ├── formatter.py
│ │ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── test_builder_registry.cpython-312.pyc
│ │ │ │ ├── test_builder.cpython-312.pyc
│ │ │ │ ├── test_css.cpython-312.pyc
│ │ │ │ ├── test_dammit.cpython-312.pyc
│ │ │ │ ├── test_docs.cpython-312.pyc
│ │ │ │ ├── test_element.cpython-312.pyc
│ │ │ │ ├── test_formatter.cpython-312.pyc
│ │ │ │ ├── test_fuzz.cpython-312.pyc
│ │ │ │ ├── test_html5lib.cpython-312.pyc
│ │ │ │ ├── test_htmlparser.cpython-312.pyc
│ │ │ │ ├── test_lxml.cpython-312.pyc
│ │ │ │ ├── test_navigablestring.cpython-312.pyc
│ │ │ │ ├── test_pageelement.cpython-312.pyc
│ │ │ │ ├── test_soup.cpython-312.pyc
│ │ │ │ ├── test_tag.cpython-312.pyc
│ │ │ │ └── test_tree.cpython-312.pyc
│ │ │ ├── fuzz
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase
│ │ │ │ ├── crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase
│ │ │ │ └── crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase
│ │ │ ├── test_builder_registry.py
│ │ │ ├── test_builder.py
│ │ │ ├── test_css.py
│ │ │ ├── test_dammit.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_element.py
│ │ │ ├── test_formatter.py
│ │ │ ├── test_fuzz.py
│ │ │ ├── test_html5lib.py
│ │ │ ├── test_htmlparser.py
│ │ │ ├── test_lxml.py
│ │ │ ├── test_navigablestring.py
│ │ │ ├── test_pageelement.py
│ │ │ ├── test_soup.py
│ │ │ ├── test_tag.py
│ │ │ └── test_tree.py
│ │ ├── certifi
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── core.cpython-312.pyc
│ │ │ ├── cacert.pem
│ │ │ ├── core.py
│ │ │ └── py.typed
│ │ ├── certifi-2024.8.30.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── charset_normalizer
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── cd.cpython-312.pyc
│ │ │ │ ├── constant.cpython-312.pyc
│ │ │ │ ├── legacy.cpython-312.pyc
│ │ │ │ ├── md.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── version.cpython-312.pyc
│ │ │ ├── api.py
│ │ │ ├── cd.py
│ │ │ ├── cli
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── __main__.cpython-312.pyc
│ │ │ ├── constant.py
│ │ │ ├── legacy.py
│ │ │ ├── md__mypyc.cpython-312-darwin.so
│ │ │ ├── md.cpython-312-darwin.so
│ │ │ ├── md.py
│ │ │ ├── models.py
│ │ │ ├── py.typed
│ │ │ ├── utils.py
│ │ │ └── version.py
│ │ ├── charset_normalizer-3.4.0.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── click
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ ├── _termui_impl.cpython-312.pyc
│ │ │ │ ├── _textwrap.cpython-312.pyc
│ │ │ │ ├── _winconsole.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── decorators.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formatting.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── shell_completion.cpython-312.pyc
│ │ │ │ ├── termui.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── types.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── _compat.py
│ │ │ ├── _termui_impl.py
│ │ │ ├── _textwrap.py
│ │ │ ├── _winconsole.py
│ │ │ ├── core.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── formatting.py
│ │ │ ├── globals.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── shell_completion.py
│ │ │ ├── termui.py
│ │ │ ├── testing.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ ├── click-8.1.7.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.rst
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── fake_useragent
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ ├── fake.cpython-312.pyc
│ │ │ │ ├── log.cpython-312.pyc
│ │ │ │ ├── settings.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── data
│ │ │ │ └── browsers.json
│ │ │ ├── errors.py
│ │ │ ├── fake.py
│ │ │ ├── log.py
│ │ │ ├── settings.py
│ │ │ └── utils.py
│ │ ├── fake_useragent-1.5.1.dist-info
│ │ │ ├── AUTHORS
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── flask
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ ├── cli.cpython-312.pyc
│ │ │ │ ├── config.cpython-312.pyc
│ │ │ │ ├── ctx.cpython-312.pyc
│ │ │ │ ├── debughelpers.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── helpers.cpython-312.pyc
│ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── signals.cpython-312.pyc
│ │ │ │ ├── templating.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── typing.cpython-312.pyc
│ │ │ │ ├── views.cpython-312.pyc
│ │ │ │ └── wrappers.cpython-312.pyc
│ │ │ ├── app.py
│ │ │ ├── blueprints.py
│ │ │ ├── cli.py
│ │ │ ├── config.py
│ │ │ ├── ctx.py
│ │ │ ├── debughelpers.py
│ │ │ ├── globals.py
│ │ │ ├── helpers.py
│ │ │ ├── json
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ └── tag.cpython-312.pyc
│ │ │ │ ├── provider.py
│ │ │ │ └── tag.py
│ │ │ ├── logging.py
│ │ │ ├── py.typed
│ │ │ ├── sansio
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ │ └── scaffold.cpython-312.pyc
│ │ │ │ ├── app.py
│ │ │ │ ├── blueprints.py
│ │ │ │ ├── README.md
│ │ │ │ └── scaffold.py
│ │ │ ├── sessions.py
│ │ │ ├── signals.py
│ │ │ ├── templating.py
│ │ │ ├── testing.py
│ │ │ ├── typing.py
│ │ │ ├── views.py
│ │ │ └── wrappers.py
│ │ ├── flask-3.0.3.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── idna
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ ├── codec.py
│ │ │ ├── compat.py
│ │ │ ├── core.py
│ │ │ ├── idnadata.py
│ │ │ ├── intranges.py
│ │ │ ├── package_data.py
│ │ │ ├── py.typed
│ │ │ └── uts46data.py
│ │ ├── idna-3.10.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── itsdangerous
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ ├── exc.cpython-312.pyc
│ │ │ │ ├── serializer.cpython-312.pyc
│ │ │ │ ├── signer.cpython-312.pyc
│ │ │ │ ├── timed.cpython-312.pyc
│ │ │ │ └── url_safe.cpython-312.pyc
│ │ │ ├── _json.py
│ │ │ ├── encoding.py
│ │ │ ├── exc.py
│ │ │ ├── py.typed
│ │ │ ├── serializer.py
│ │ │ ├── signer.py
│ │ │ ├── timed.py
│ │ │ └── url_safe.py
│ │ ├── itsdangerous-2.2.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── jinja2
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _identifier.cpython-312.pyc
│ │ │ │ ├── async_utils.cpython-312.pyc
│ │ │ │ ├── bccache.cpython-312.pyc
│ │ │ │ ├── compiler.cpython-312.pyc
│ │ │ │ ├── constants.cpython-312.pyc
│ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ ├── defaults.cpython-312.pyc
│ │ │ │ ├── environment.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ ├── filters.cpython-312.pyc
│ │ │ │ ├── idtracking.cpython-312.pyc
│ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ ├── loaders.cpython-312.pyc
│ │ │ │ ├── meta.cpython-312.pyc
│ │ │ │ ├── nativetypes.cpython-312.pyc
│ │ │ │ ├── nodes.cpython-312.pyc
│ │ │ │ ├── optimizer.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── runtime.cpython-312.pyc
│ │ │ │ ├── sandbox.cpython-312.pyc
│ │ │ │ ├── tests.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── visitor.cpython-312.pyc
│ │ │ ├── _identifier.py
│ │ │ ├── async_utils.py
│ │ │ ├── bccache.py
│ │ │ ├── compiler.py
│ │ │ ├── constants.py
│ │ │ ├── debug.py
│ │ │ ├── defaults.py
│ │ │ ├── environment.py
│ │ │ ├── exceptions.py
│ │ │ ├── ext.py
│ │ │ ├── filters.py
│ │ │ ├── idtracking.py
│ │ │ ├── lexer.py
│ │ │ ├── loaders.py
│ │ │ ├── meta.py
│ │ │ ├── nativetypes.py
│ │ │ ├── nodes.py
│ │ │ ├── optimizer.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── runtime.py
│ │ │ ├── sandbox.py
│ │ │ ├── tests.py
│ │ │ ├── utils.py
│ │ │ └── visitor.py
│ │ ├── jinja2-3.1.4.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── lxml
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _elementpath.cpython-312.pyc
│ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ ├── cssselect.cpython-312.pyc
│ │ │ │ ├── doctestcompare.cpython-312.pyc
│ │ │ │ ├── ElementInclude.cpython-312.pyc
│ │ │ │ ├── pyclasslookup.cpython-312.pyc
│ │ │ │ ├── sax.cpython-312.pyc
│ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ ├── _elementpath.cpython-312-darwin.so
│ │ │ ├── _elementpath.py
│ │ │ ├── apihelpers.pxi
│ │ │ ├── builder.cpython-312-darwin.so
│ │ │ ├── builder.py
│ │ │ ├── classlookup.pxi
│ │ │ ├── cleanup.pxi
│ │ │ ├── cssselect.py
│ │ │ ├── debug.pxi
│ │ │ ├── docloader.pxi
│ │ │ ├── doctestcompare.py
│ │ │ ├── dtd.pxi
│ │ │ ├── ElementInclude.py
│ │ │ ├── etree_api.h
│ │ │ ├── etree.cpython-312-darwin.so
│ │ │ ├── etree.h
│ │ │ ├── etree.pyx
│ │ │ ├── extensions.pxi
│ │ │ ├── html
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _diffcommand.cpython-312.pyc
│ │ │ │ │ ├── _html5builder.cpython-312.pyc
│ │ │ │ │ ├── _setmixin.cpython-312.pyc
│ │ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ │ ├── clean.cpython-312.pyc
│ │ │ │ │ ├── defs.cpython-312.pyc
│ │ │ │ │ ├── diff.cpython-312.pyc
│ │ │ │ │ ├── ElementSoup.cpython-312.pyc
│ │ │ │ │ ├── formfill.cpython-312.pyc
│ │ │ │ │ ├── html5parser.cpython-312.pyc
│ │ │ │ │ ├── soupparser.cpython-312.pyc
│ │ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ │ ├── _diffcommand.py
│ │ │ │ ├── _html5builder.py
│ │ │ │ ├── _setmixin.py
│ │ │ │ ├── builder.py
│ │ │ │ ├── clean.py
│ │ │ │ ├── defs.py
│ │ │ │ ├── diff.cpython-312-darwin.so
│ │ │ │ ├── diff.py
│ │ │ │ ├── ElementSoup.py
│ │ │ │ ├── formfill.py
│ │ │ │ ├── html5parser.py
│ │ │ │ ├── soupparser.py
│ │ │ │ └── usedoctest.py
│ │ │ ├── includes
│ │ │ │ ├── __init__.pxd
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── c14n.pxd
│ │ │ │ ├── config.pxd
│ │ │ │ ├── dtdvalid.pxd
│ │ │ │ ├── etree_defs.h
│ │ │ │ ├── etreepublic.pxd
│ │ │ │ ├── extlibs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── libcharset.h
│ │ │ │ │ ├── localcharset.h
│ │ │ │ │ ├── zconf.h
│ │ │ │ │ └── zlib.h
│ │ │ │ ├── htmlparser.pxd
│ │ │ │ ├── libexslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── exslt.h
│ │ │ │ │ ├── exsltconfig.h
│ │ │ │ │ └── exsltexports.h
│ │ │ │ ├── libxml
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── c14n.h
│ │ │ │ │ ├── catalog.h
│ │ │ │ │ ├── chvalid.h
│ │ │ │ │ ├── debugXML.h
│ │ │ │ │ ├── dict.h
│ │ │ │ │ ├── encoding.h
│ │ │ │ │ ├── entities.h
│ │ │ │ │ ├── globals.h
│ │ │ │ │ ├── hash.h
│ │ │ │ │ ├── HTMLparser.h
│ │ │ │ │ ├── HTMLtree.h
│ │ │ │ │ ├── list.h
│ │ │ │ │ ├── nanoftp.h
│ │ │ │ │ ├── nanohttp.h
│ │ │ │ │ ├── parser.h
│ │ │ │ │ ├── parserInternals.h
│ │ │ │ │ ├── relaxng.h
│ │ │ │ │ ├── SAX.h
│ │ │ │ │ ├── SAX2.h
│ │ │ │ │ ├── schemasInternals.h
│ │ │ │ │ ├── schematron.h
│ │ │ │ │ ├── threads.h
│ │ │ │ │ ├── tree.h
│ │ │ │ │ ├── uri.h
│ │ │ │ │ ├── valid.h
│ │ │ │ │ ├── xinclude.h
│ │ │ │ │ ├── xlink.h
│ │ │ │ │ ├── xmlautomata.h
│ │ │ │ │ ├── xmlerror.h
│ │ │ │ │ ├── xmlexports.h
│ │ │ │ │ ├── xmlIO.h
│ │ │ │ │ ├── xmlmemory.h
│ │ │ │ │ ├── xmlmodule.h
│ │ │ │ │ ├── xmlreader.h
│ │ │ │ │ ├── xmlregexp.h
│ │ │ │ │ ├── xmlsave.h
│ │ │ │ │ ├── xmlschemas.h
│ │ │ │ │ ├── xmlschemastypes.h
│ │ │ │ │ ├── xmlstring.h
│ │ │ │ │ ├── xmlunicode.h
│ │ │ │ │ ├── xmlversion.h
│ │ │ │ │ ├── xmlwriter.h
│ │ │ │ │ ├── xpath.h
│ │ │ │ │ ├── xpathInternals.h
│ │ │ │ │ └── xpointer.h
│ │ │ │ ├── libxslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── attributes.h
│ │ │ │ │ ├── documents.h
│ │ │ │ │ ├── extensions.h
│ │ │ │ │ ├── extra.h
│ │ │ │ │ ├── functions.h
│ │ │ │ │ ├── imports.h
│ │ │ │ │ ├── keys.h
│ │ │ │ │ ├── namespaces.h
│ │ │ │ │ ├── numbersInternals.h
│ │ │ │ │ ├── pattern.h
│ │ │ │ │ ├── preproc.h
│ │ │ │ │ ├── security.h
│ │ │ │ │ ├── templates.h
│ │ │ │ │ ├── transform.h
│ │ │ │ │ ├── variables.h
│ │ │ │ │ ├── xslt.h
│ │ │ │ │ ├── xsltconfig.h
│ │ │ │ │ ├── xsltexports.h
│ │ │ │ │ ├── xsltInternals.h
│ │ │ │ │ ├── xsltlocale.h
│ │ │ │ │ └── xsltutils.h
│ │ │ │ ├── lxml-version.h
│ │ │ │ ├── relaxng.pxd
│ │ │ │ ├── schematron.pxd
│ │ │ │ ├── tree.pxd
│ │ │ │ ├── uri.pxd
│ │ │ │ ├── xinclude.pxd
│ │ │ │ ├── xmlerror.pxd
│ │ │ │ ├── xmlparser.pxd
│ │ │ │ ├── xmlschema.pxd
│ │ │ │ ├── xpath.pxd
│ │ │ │ └── xslt.pxd
│ │ │ ├── isoschematron
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ └── resources
│ │ │ │ ├── rng
│ │ │ │ │ └── iso-schematron.rng
│ │ │ │ └── xsl
│ │ │ │ ├── iso-schematron-xslt1
│ │ │ │ │ ├── iso_abstract_expand.xsl
│ │ │ │ │ ├── iso_dsdl_include.xsl
│ │ │ │ │ ├── iso_schematron_message.xsl
│ │ │ │ │ ├── iso_schematron_skeleton_for_xslt1.xsl
│ │ │ │ │ ├── iso_svrl_for_xslt1.xsl
│ │ │ │ │ └── readme.txt
│ │ │ │ ├── RNG2Schtrn.xsl
│ │ │ │ └── XSD2Schtrn.xsl
│ │ │ ├── iterparse.pxi
│ │ │ ├── lxml.etree_api.h
│ │ │ ├── lxml.etree.h
│ │ │ ├── nsclasses.pxi
│ │ │ ├── objectify.cpython-312-darwin.so
│ │ │ ├── objectify.pyx
│ │ │ ├── objectpath.pxi
│ │ │ ├── parser.pxi
│ │ │ ├── parsertarget.pxi
│ │ │ ├── proxy.pxi
│ │ │ ├── public-api.pxi
│ │ │ ├── pyclasslookup.py
│ │ │ ├── readonlytree.pxi
│ │ │ ├── relaxng.pxi
│ │ │ ├── sax.cpython-312-darwin.so
│ │ │ ├── sax.py
│ │ │ ├── saxparser.pxi
│ │ │ ├── schematron.pxi
│ │ │ ├── serializer.pxi
│ │ │ ├── usedoctest.py
│ │ │ ├── xinclude.pxi
│ │ │ ├── xmlerror.pxi
│ │ │ ├── xmlid.pxi
│ │ │ ├── xmlschema.pxi
│ │ │ ├── xpath.pxi
│ │ │ ├── xslt.pxi
│ │ │ └── xsltext.pxi
│ │ ├── lxml-5.3.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── LICENSES.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── markupsafe
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── _native.cpython-312.pyc
│ │ │ ├── _native.py
│ │ │ ├── _speedups.c
│ │ │ ├── _speedups.cpython-312-darwin.so
│ │ │ ├── _speedups.pyi
│ │ │ └── py.typed
│ │ ├── MarkupSafe-3.0.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── pip
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pip-runner__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── __pip-runner__.cpython-312.pyc
│ │ │ ├── _internal
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── build_env.cpython-312.pyc
│ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ ├── pyproject.cpython-312.pyc
│ │ │ │ │ ├── self_outdated_check.cpython-312.pyc
│ │ │ │ │ └── wheel_builder.cpython-312.pyc
│ │ │ │ ├── build_env.py
│ │ │ │ ├── cache.py
│ │ │ │ ├── cli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── autocompletion.cpython-312.pyc
│ │ │ │ │ │ ├── base_command.cpython-312.pyc
│ │ │ │ │ │ ├── cmdoptions.cpython-312.pyc
│ │ │ │ │ │ ├── command_context.cpython-312.pyc
│ │ │ │ │ │ ├── index_command.cpython-312.pyc
│ │ │ │ │ │ ├── main_parser.cpython-312.pyc
│ │ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bars.cpython-312.pyc
│ │ │ │ │ │ ├── req_command.cpython-312.pyc
│ │ │ │ │ │ ├── spinners.cpython-312.pyc
│ │ │ │ │ │ └── status_codes.cpython-312.pyc
│ │ │ │ │ ├── autocompletion.py
│ │ │ │ │ ├── base_command.py
│ │ │ │ │ ├── cmdoptions.py
│ │ │ │ │ ├── command_context.py
│ │ │ │ │ ├── index_command.py
│ │ │ │ │ ├── main_parser.py
│ │ │ │ │ ├── main.py
│ │ │ │ │ ├── parser.py
│ │ │ │ │ ├── progress_bars.py
│ │ │ │ │ ├── req_command.py
│ │ │ │ │ ├── spinners.py
│ │ │ │ │ └── status_codes.py
│ │ │ │ ├── commands
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── completion.cpython-312.pyc
│ │ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ ├── hash.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── inspect.cpython-312.pyc
│ │ │ │ │ │ ├── install.cpython-312.pyc
│ │ │ │ │ │ ├── list.cpython-312.pyc
│ │ │ │ │ │ ├── search.cpython-312.pyc
│ │ │ │ │ │ ├── show.cpython-312.pyc
│ │ │ │ │ │ ├── uninstall.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── completion.py
│ │ │ │ │ ├── configuration.py
│ │ │ │ │ ├── debug.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── hash.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── inspect.py
│ │ │ │ │ ├── install.py
│ │ │ │ │ ├── list.py
│ │ │ │ │ ├── search.py
│ │ │ │ │ ├── show.py
│ │ │ │ │ ├── uninstall.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── configuration.py
│ │ │ │ ├── distributions
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── installed.cpython-312.pyc
│ │ │ │ │ │ ├── sdist.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── installed.py
│ │ │ │ │ ├── sdist.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── index
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── collector.cpython-312.pyc
│ │ │ │ │ │ ├── package_finder.cpython-312.pyc
│ │ │ │ │ │ └── sources.cpython-312.pyc
│ │ │ │ │ ├── collector.py
│ │ │ │ │ ├── package_finder.py
│ │ │ │ │ └── sources.py
│ │ │ │ ├── locations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _distutils.cpython-312.pyc
│ │ │ │ │ │ ├── _sysconfig.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── _distutils.py
│ │ │ │ │ ├── _sysconfig.py
│ │ │ │ │ └── base.py
│ │ │ │ ├── main.py
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ └── pkg_resources.cpython-312.pyc
│ │ │ │ │ ├── _json.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── importlib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ │ ├── _dists.cpython-312.pyc
│ │ │ │ │ │ │ └── _envs.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.py
│ │ │ │ │ │ ├── _dists.py
│ │ │ │ │ │ └── _envs.py
│ │ │ │ │ └── pkg_resources.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── candidate.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url.cpython-312.pyc
│ │ │ │ │ │ ├── format_control.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── installation_report.cpython-312.pyc
│ │ │ │ │ │ ├── link.cpython-312.pyc
│ │ │ │ │ │ ├── scheme.cpython-312.pyc
│ │ │ │ │ │ ├── search_scope.cpython-312.pyc
│ │ │ │ │ │ ├── selection_prefs.cpython-312.pyc
│ │ │ │ │ │ ├── target_python.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── candidate.py
│ │ │ │ │ ├── direct_url.py
│ │ │ │ │ ├── format_control.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── installation_report.py
│ │ │ │ │ ├── link.py
│ │ │ │ │ ├── scheme.py
│ │ │ │ │ ├── search_scope.py
│ │ │ │ │ ├── selection_prefs.py
│ │ │ │ │ ├── target_python.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── network
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── lazy_wheel.cpython-312.pyc
│ │ │ │ │ │ ├── session.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── xmlrpc.cpython-312.pyc
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── lazy_wheel.py
│ │ │ │ │ ├── session.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── xmlrpc.py
│ │ │ │ ├── operations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ └── prepare.cpython-312.pyc
│ │ │ │ │ ├── build
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── build_tracker.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_legacy.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── build_tracker.py
│ │ │ │ │ │ ├── metadata_editable.py
│ │ │ │ │ │ ├── metadata_legacy.py
│ │ │ │ │ │ ├── metadata.py
│ │ │ │ │ │ ├── wheel_editable.py
│ │ │ │ │ │ ├── wheel_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── install
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── editable_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── editable_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ └── prepare.py
│ │ │ │ ├── pyproject.py
│ │ │ │ ├── req
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── constructors.cpython-312.pyc
│ │ │ │ │ │ ├── req_file.cpython-312.pyc
│ │ │ │ │ │ ├── req_install.cpython-312.pyc
│ │ │ │ │ │ ├── req_set.cpython-312.pyc
│ │ │ │ │ │ └── req_uninstall.cpython-312.pyc
│ │ │ │ │ ├── constructors.py
│ │ │ │ │ ├── req_file.py
│ │ │ │ │ ├── req_install.py
│ │ │ │ │ ├── req_set.py
│ │ │ │ │ └── req_uninstall.py
│ │ │ │ ├── resolution
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ │ └── resolver.py
│ │ │ │ │ └── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── candidates.cpython-312.pyc
│ │ │ │ │ │ ├── factory.cpython-312.pyc
│ │ │ │ │ │ ├── found_candidates.cpython-312.pyc
│ │ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ │ ├── reporter.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── candidates.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── found_candidates.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ ├── reporter.py
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ └── resolver.py
│ │ │ │ ├── self_outdated_check.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _jaraco_text.cpython-312.pyc
│ │ │ │ │ │ ├── _log.cpython-312.pyc
│ │ │ │ │ │ ├── appdirs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── compatibility_tags.cpython-312.pyc
│ │ │ │ │ │ ├── datetime.cpython-312.pyc
│ │ │ │ │ │ ├── deprecation.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url_helpers.cpython-312.pyc
│ │ │ │ │ │ ├── egg_link.cpython-312.pyc
│ │ │ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ │ │ ├── entrypoints.cpython-312.pyc
│ │ │ │ │ │ ├── filesystem.cpython-312.pyc
│ │ │ │ │ │ ├── filetypes.cpython-312.pyc
│ │ │ │ │ │ ├── glibc.cpython-312.pyc
│ │ │ │ │ │ ├── hashes.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── misc.cpython-312.pyc
│ │ │ │ │ │ ├── packaging.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── setuptools_build.cpython-312.pyc
│ │ │ │ │ │ ├── subprocess.cpython-312.pyc
│ │ │ │ │ │ ├── temp_dir.cpython-312.pyc
│ │ │ │ │ │ ├── unpacking.cpython-312.pyc
│ │ │ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ │ │ ├── virtualenv.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── _jaraco_text.py
│ │ │ │ │ ├── _log.py
│ │ │ │ │ ├── appdirs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── compatibility_tags.py
│ │ │ │ │ ├── datetime.py
│ │ │ │ │ ├── deprecation.py
│ │ │ │ │ ├── direct_url_helpers.py
│ │ │ │ │ ├── egg_link.py
│ │ │ │ │ ├── encoding.py
│ │ │ │ │ ├── entrypoints.py
│ │ │ │ │ ├── filesystem.py
│ │ │ │ │ ├── filetypes.py
│ │ │ │ │ ├── glibc.py
│ │ │ │ │ ├── hashes.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── misc.py
│ │ │ │ │ ├── packaging.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── setuptools_build.py
│ │ │ │ │ ├── subprocess.py
│ │ │ │ │ ├── temp_dir.py
│ │ │ │ │ ├── unpacking.py
│ │ │ │ │ ├── urls.py
│ │ │ │ │ ├── virtualenv.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── vcs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── bazaar.cpython-312.pyc
│ │ │ │ │ │ ├── git.cpython-312.pyc
│ │ │ │ │ │ ├── mercurial.cpython-312.pyc
│ │ │ │ │ │ ├── subversion.cpython-312.pyc
│ │ │ │ │ │ └── versioncontrol.cpython-312.pyc
│ │ │ │ │ ├── bazaar.py
│ │ │ │ │ ├── git.py
│ │ │ │ │ ├── mercurial.py
│ │ │ │ │ ├── subversion.py
│ │ │ │ │ └── versioncontrol.py
│ │ │ │ └── wheel_builder.py
│ │ │ ├── _vendor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ └── typing_extensions.cpython-312.pyc
│ │ │ │ ├── cachecontrol
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _cmd.cpython-312.pyc
│ │ │ │ │ │ ├── adapter.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── controller.cpython-312.pyc
│ │ │ │ │ │ ├── filewrapper.cpython-312.pyc
│ │ │ │ │ │ ├── heuristics.cpython-312.pyc
│ │ │ │ │ │ ├── serialize.cpython-312.pyc
│ │ │ │ │ │ └── wrapper.cpython-312.pyc
│ │ │ │ │ ├── _cmd.py
│ │ │ │ │ ├── adapter.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── caches
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── file_cache.cpython-312.pyc
│ │ │ │ │ │ │ └── redis_cache.cpython-312.pyc
│ │ │ │ │ │ ├── file_cache.py
│ │ │ │ │ │ └── redis_cache.py
│ │ │ │ │ ├── controller.py
│ │ │ │ │ ├── filewrapper.py
│ │ │ │ │ ├── heuristics.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── serialize.py
│ │ │ │ │ └── wrapper.py
│ │ │ │ ├── certifi
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── core.cpython-312.pyc
│ │ │ │ │ ├── cacert.pem
│ │ │ │ │ ├── core.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── distlib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── database.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── locators.cpython-312.pyc
│ │ │ │ │ │ ├── manifest.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── resources.cpython-312.pyc
│ │ │ │ │ │ ├── scripts.cpython-312.pyc
│ │ │ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── database.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── locators.py
│ │ │ │ │ ├── manifest.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ ├── scripts.py
│ │ │ │ │ ├── t32.exe
│ │ │ │ │ ├── t64-arm.exe
│ │ │ │ │ ├── t64.exe
│ │ │ │ │ ├── util.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ ├── w32.exe
│ │ │ │ │ ├── w64-arm.exe
│ │ │ │ │ ├── w64.exe
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── distro
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── distro.cpython-312.pyc
│ │ │ │ │ ├── distro.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── idna
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ │ │ ├── codec.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── core.py
│ │ │ │ │ ├── idnadata.py
│ │ │ │ │ ├── intranges.py
│ │ │ │ │ ├── package_data.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ └── uts46data.py
│ │ │ │ ├── msgpack
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ │ │ └── fallback.cpython-312.pyc
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── ext.py
│ │ │ │ │ └── fallback.py
│ │ │ │ ├── packaging
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _elffile.cpython-312.pyc
│ │ │ │ │ │ ├── _manylinux.cpython-312.pyc
│ │ │ │ │ │ ├── _musllinux.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _structures.cpython-312.pyc
│ │ │ │ │ │ ├── _tokenizer.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ ├── specifiers.cpython-312.pyc
│ │ │ │ │ │ ├── tags.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── version.cpython-312.pyc
│ │ │ │ │ ├── _elffile.py
│ │ │ │ │ ├── _manylinux.py
│ │ │ │ │ ├── _musllinux.py
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _structures.py
│ │ │ │ │ ├── _tokenizer.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ ├── specifiers.py
│ │ │ │ │ ├── tags.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── version.py
│ │ │ │ ├── pkg_resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── platformdirs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── android.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── macos.cpython-312.pyc
│ │ │ │ │ │ ├── unix.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── windows.cpython-312.pyc
│ │ │ │ │ ├── android.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── unix.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── pygments
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── cmdline.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── filter.cpython-312.pyc
│ │ │ │ │ │ ├── formatter.cpython-312.pyc
│ │ │ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ │ │ ├── modeline.cpython-312.pyc
│ │ │ │ │ │ ├── plugin.cpython-312.pyc
│ │ │ │ │ │ ├── regexopt.cpython-312.pyc
│ │ │ │ │ │ ├── scanner.cpython-312.pyc
│ │ │ │ │ │ ├── sphinxext.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── token.cpython-312.pyc
│ │ │ │ │ │ ├── unistring.cpython-312.pyc
│ │ │ │ │ │ └── util.cpython-312.pyc
│ │ │ │ │ ├── cmdline.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── filter.py
│ │ │ │ │ ├── filters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── formatter.py
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ ├── bbcode.cpython-312.pyc
│ │ │ │ │ │ │ ├── groff.cpython-312.pyc
│ │ │ │ │ │ │ ├── html.cpython-312.pyc
│ │ │ │ │ │ │ ├── img.cpython-312.pyc
│ │ │ │ │ │ │ ├── irc.cpython-312.pyc
│ │ │ │ │ │ │ ├── latex.cpython-312.pyc
│ │ │ │ │ │ │ ├── other.cpython-312.pyc
│ │ │ │ │ │ │ ├── pangomarkup.cpython-312.pyc
│ │ │ │ │ │ │ ├── rtf.cpython-312.pyc
│ │ │ │ │ │ │ ├── svg.cpython-312.pyc
│ │ │ │ │ │ │ ├── terminal.cpython-312.pyc
│ │ │ │ │ │ │ └── terminal256.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ ├── bbcode.py
│ │ │ │ │ │ ├── groff.py
│ │ │ │ │ │ ├── html.py
│ │ │ │ │ │ ├── img.py
│ │ │ │ │ │ ├── irc.py
│ │ │ │ │ │ ├── latex.py
│ │ │ │ │ │ ├── other.py
│ │ │ │ │ │ ├── pangomarkup.py
│ │ │ │ │ │ ├── rtf.py
│ │ │ │ │ │ ├── svg.py
│ │ │ │ │ │ ├── terminal.py
│ │ │ │ │ │ └── terminal256.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ ├── lexers
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ └── python.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ └── python.py
│ │ │ │ │ ├── modeline.py
│ │ │ │ │ ├── plugin.py
│ │ │ │ │ ├── regexopt.py
│ │ │ │ │ ├── scanner.py
│ │ │ │ │ ├── sphinxext.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styles
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── _mapping.cpython-312.pyc
│ │ │ │ │ │ └── _mapping.py
│ │ │ │ │ ├── token.py
│ │ │ │ │ ├── unistring.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── pyproject_hooks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ └── _impl.cpython-312.pyc
│ │ │ │ │ ├── _compat.py
│ │ │ │ │ ├── _impl.py
│ │ │ │ │ └── _in_process
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── _in_process.cpython-312.pyc
│ │ │ │ │ └── _in_process.py
│ │ │ │ ├── requests
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ │ ├── __version__.py
│ │ │ │ │ ├── _internal_utils.py
│ │ │ │ │ ├── adapters.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── certs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── cookies.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── hooks.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ ├── packages.py
│ │ │ │ │ ├── sessions.py
│ │ │ │ │ ├── status_codes.py
│ │ │ │ │ ├── structures.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── providers.cpython-312.pyc
│ │ │ │ │ │ ├── reporters.cpython-312.pyc
│ │ │ │ │ │ ├── resolvers.cpython-312.pyc
│ │ │ │ │ │ └── structs.cpython-312.pyc
│ │ │ │ │ ├── compat
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── collections_abc.cpython-312.pyc
│ │ │ │ │ │ └── collections_abc.py
│ │ │ │ │ ├── providers.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── reporters.py
│ │ │ │ │ ├── resolvers.py
│ │ │ │ │ └── structs.py
│ │ │ │ ├── rich
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── _cell_widths.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_codes.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_replace.cpython-312.pyc
│ │ │ │ │ │ ├── _export_format.cpython-312.pyc
│ │ │ │ │ │ ├── _extension.cpython-312.pyc
│ │ │ │ │ │ ├── _fileno.cpython-312.pyc
│ │ │ │ │ │ ├── _inspect.cpython-312.pyc
│ │ │ │ │ │ ├── _log_render.cpython-312.pyc
│ │ │ │ │ │ ├── _loop.cpython-312.pyc
│ │ │ │ │ │ ├── _null_file.cpython-312.pyc
│ │ │ │ │ │ ├── _palettes.cpython-312.pyc
│ │ │ │ │ │ ├── _pick.cpython-312.pyc
│ │ │ │ │ │ ├── _ratio.cpython-312.pyc
│ │ │ │ │ │ ├── _spinners.cpython-312.pyc
│ │ │ │ │ │ ├── _stack.cpython-312.pyc
│ │ │ │ │ │ ├── _timer.cpython-312.pyc
│ │ │ │ │ │ ├── _win32_console.cpython-312.pyc
│ │ │ │ │ │ ├── _windows_renderer.cpython-312.pyc
│ │ │ │ │ │ ├── _windows.cpython-312.pyc
│ │ │ │ │ │ ├── _wrap.cpython-312.pyc
│ │ │ │ │ │ ├── abc.cpython-312.pyc
│ │ │ │ │ │ ├── align.cpython-312.pyc
│ │ │ │ │ │ ├── ansi.cpython-312.pyc
│ │ │ │ │ │ ├── bar.cpython-312.pyc
│ │ │ │ │ │ ├── box.cpython-312.pyc
│ │ │ │ │ │ ├── cells.cpython-312.pyc
│ │ │ │ │ │ ├── color_triplet.cpython-312.pyc
│ │ │ │ │ │ ├── color.cpython-312.pyc
│ │ │ │ │ │ ├── columns.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── constrain.cpython-312.pyc
│ │ │ │ │ │ ├── containers.cpython-312.pyc
│ │ │ │ │ │ ├── control.cpython-312.pyc
│ │ │ │ │ │ ├── default_styles.cpython-312.pyc
│ │ │ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ │ │ ├── emoji.cpython-312.pyc
│ │ │ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ │ │ ├── file_proxy.cpython-312.pyc
│ │ │ │ │ │ ├── filesize.cpython-312.pyc
│ │ │ │ │ │ ├── highlighter.cpython-312.pyc
│ │ │ │ │ │ ├── json.cpython-312.pyc
│ │ │ │ │ │ ├── jupyter.cpython-312.pyc
│ │ │ │ │ │ ├── layout.cpython-312.pyc
│ │ │ │ │ │ ├── live_render.cpython-312.pyc
│ │ │ │ │ │ ├── live.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── markup.cpython-312.pyc
│ │ │ │ │ │ ├── measure.cpython-312.pyc
│ │ │ │ │ │ ├── padding.cpython-312.pyc
│ │ │ │ │ │ ├── pager.cpython-312.pyc
│ │ │ │ │ │ ├── palette.cpython-312.pyc
│ │ │ │ │ │ ├── panel.cpython-312.pyc
│ │ │ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bar.cpython-312.pyc
│ │ │ │ │ │ ├── progress.cpython-312.pyc
│ │ │ │ │ │ ├── prompt.cpython-312.pyc
│ │ │ │ │ │ ├── protocol.cpython-312.pyc
│ │ │ │ │ │ ├── region.cpython-312.pyc
│ │ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ │ ├── rule.cpython-312.pyc
│ │ │ │ │ │ ├── scope.cpython-312.pyc
│ │ │ │ │ │ ├── screen.cpython-312.pyc
│ │ │ │ │ │ ├── segment.cpython-312.pyc
│ │ │ │ │ │ ├── spinner.cpython-312.pyc
│ │ │ │ │ │ ├── status.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── styled.cpython-312.pyc
│ │ │ │ │ │ ├── syntax.cpython-312.pyc
│ │ │ │ │ │ ├── table.cpython-312.pyc
│ │ │ │ │ │ ├── terminal_theme.cpython-312.pyc
│ │ │ │ │ │ ├── text.cpython-312.pyc
│ │ │ │ │ │ ├── theme.cpython-312.pyc
│ │ │ │ │ │ ├── themes.cpython-312.pyc
│ │ │ │ │ │ ├── traceback.cpython-312.pyc
│ │ │ │ │ │ └── tree.cpython-312.pyc
│ │ │ │ │ ├── _cell_widths.py
│ │ │ │ │ ├── _emoji_codes.py
│ │ │ │ │ ├── _emoji_replace.py
│ │ │ │ │ ├── _export_format.py
│ │ │ │ │ ├── _extension.py
│ │ │ │ │ ├── _fileno.py
│ │ │ │ │ ├── _inspect.py
│ │ │ │ │ ├── _log_render.py
│ │ │ │ │ ├── _loop.py
│ │ │ │ │ ├── _null_file.py
│ │ │ │ │ ├── _palettes.py
│ │ │ │ │ ├── _pick.py
│ │ │ │ │ ├── _ratio.py
│ │ │ │ │ ├── _spinners.py
│ │ │ │ │ ├── _stack.py
│ │ │ │ │ ├── _timer.py
│ │ │ │ │ ├── _win32_console.py
│ │ │ │ │ ├── _windows_renderer.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ ├── _wrap.py
│ │ │ │ │ ├── abc.py
│ │ │ │ │ ├── align.py
│ │ │ │ │ ├── ansi.py
│ │ │ │ │ ├── bar.py
│ │ │ │ │ ├── box.py
│ │ │ │ │ ├── cells.py
│ │ │ │ │ ├── color_triplet.py
│ │ │ │ │ ├── color.py
│ │ │ │ │ ├── columns.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── constrain.py
│ │ │ │ │ ├── containers.py
│ │ │ │ │ ├── control.py
│ │ │ │ │ ├── default_styles.py
│ │ │ │ │ ├── diagnose.py
│ │ │ │ │ ├── emoji.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_proxy.py
│ │ │ │ │ ├── filesize.py
│ │ │ │ │ ├── highlighter.py
│ │ │ │ │ ├── json.py
│ │ │ │ │ ├── jupyter.py
│ │ │ │ │ ├── layout.py
│ │ │ │ │ ├── live_render.py
│ │ │ │ │ ├── live.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── markup.py
│ │ │ │ │ ├── measure.py
│ │ │ │ │ ├── padding.py
│ │ │ │ │ ├── pager.py
│ │ │ │ │ ├── palette.py
│ │ │ │ │ ├── panel.py
│ │ │ │ │ ├── pretty.py
│ │ │ │ │ ├── progress_bar.py
│ │ │ │ │ ├── progress.py
│ │ │ │ │ ├── prompt.py
│ │ │ │ │ ├── protocol.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── region.py
│ │ │ │ │ ├── repr.py
│ │ │ │ │ ├── rule.py
│ │ │ │ │ ├── scope.py
│ │ │ │ │ ├── screen.py
│ │ │ │ │ ├── segment.py
│ │ │ │ │ ├── spinner.py
│ │ │ │ │ ├── status.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styled.py
│ │ │ │ │ ├── syntax.py
│ │ │ │ │ ├── table.py
│ │ │ │ │ ├── terminal_theme.py
│ │ │ │ │ ├── text.py
│ │ │ │ │ ├── theme.py
│ │ │ │ │ ├── themes.py
│ │ │ │ │ ├── traceback.py
│ │ │ │ │ └── tree.py
│ │ │ │ ├── tomli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _re.cpython-312.pyc
│ │ │ │ │ │ └── _types.cpython-312.pyc
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _re.py
│ │ │ │ │ ├── _types.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── truststore
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _api.cpython-312.pyc
│ │ │ │ │ │ ├── _macos.cpython-312.pyc
│ │ │ │ │ │ ├── _openssl.cpython-312.pyc
│ │ │ │ │ │ ├── _ssl_constants.cpython-312.pyc
│ │ │ │ │ │ └── _windows.cpython-312.pyc
│ │ │ │ │ ├── _api.py
│ │ │ │ │ ├── _macos.py
│ │ │ │ │ ├── _openssl.py
│ │ │ │ │ ├── _ssl_constants.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── typing_extensions.py
│ │ │ │ ├── urllib3
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── _collections.py
│ │ │ │ │ ├── _version.py
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── connectionpool.py
│ │ │ │ │ ├── contrib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _appengine_environ.cpython-312.pyc
│ │ │ │ │ │ │ ├── appengine.cpython-312.pyc
│ │ │ │ │ │ │ ├── ntlmpool.cpython-312.pyc
│ │ │ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ │ │ ├── securetransport.cpython-312.pyc
│ │ │ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ │ │ ├── _appengine_environ.py
│ │ │ │ │ │ ├── _securetransport
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── bindings.cpython-312.pyc
│ │ │ │ │ │ │ │ └── low_level.cpython-312.pyc
│ │ │ │ │ │ │ ├── bindings.py
│ │ │ │ │ │ │ └── low_level.py
│ │ │ │ │ │ ├── appengine.py
│ │ │ │ │ │ ├── ntlmpool.py
│ │ │ │ │ │ ├── pyopenssl.py
│ │ │ │ │ │ ├── securetransport.py
│ │ │ │ │ │ └── socks.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── fields.py
│ │ │ │ │ ├── filepost.py
│ │ │ │ │ ├── packages
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── six.cpython-312.pyc
│ │ │ │ │ │ ├── backports
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── makefile.cpython-312.pyc
│ │ │ │ │ │ │ │ └── weakref_finalize.cpython-312.pyc
│ │ │ │ │ │ │ ├── makefile.py
│ │ │ │ │ │ │ └── weakref_finalize.py
│ │ │ │ │ │ └── six.py
│ │ │ │ │ ├── poolmanager.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ └── util
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ │ │ ├── queue.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ │ │ └── wait.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── proxy.py
│ │ │ │ │ ├── queue.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── ssl_.py
│ │ │ │ │ ├── ssl_match_hostname.py
│ │ │ │ │ ├── ssltransport.py
│ │ │ │ │ ├── timeout.py
│ │ │ │ │ ├── url.py
│ │ │ │ │ └── wait.py
│ │ │ │ └── vendor.txt
│ │ │ └── py.typed
│ │ ├── pip-24.2.dist-info
│ │ │ ├── AUTHORS.txt
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── requests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── __version__.py
│ │ │ ├── _internal_utils.py
│ │ │ ├── adapters.py
│ │ │ ├── api.py
│ │ │ ├── auth.py
│ │ │ ├── certs.py
│ │ │ ├── compat.py
│ │ │ ├── cookies.py
│ │ │ ├── exceptions.py
│ │ │ ├── help.py
│ │ │ ├── hooks.py
│ │ │ ├── models.py
│ │ │ ├── packages.py
│ │ │ ├── sessions.py
│ │ │ ├── status_codes.py
│ │ │ ├── structures.py
│ │ │ └── utils.py
│ │ ├── requests-2.32.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── soupsieve
│ │ │ ├── __init__.py
│ │ │ ├── __meta__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __meta__.cpython-312.pyc
│ │ │ │ ├── css_match.cpython-312.pyc
│ │ │ │ ├── css_parser.cpython-312.pyc
│ │ │ │ ├── css_types.cpython-312.pyc
│ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ └── util.cpython-312.pyc
│ │ │ ├── css_match.py
│ │ │ ├── css_parser.py
│ │ │ ├── css_types.py
│ │ │ ├── pretty.py
│ │ │ ├── py.typed
│ │ │ └── util.py
│ │ ├── soupsieve-2.6.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── urllib3
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _base_connection.cpython-312.pyc
│ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ ├── _request_methods.cpython-312.pyc
│ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ └── response.cpython-312.pyc
│ │ │ ├── _base_connection.py
│ │ │ ├── _collections.py
│ │ │ ├── _request_methods.py
│ │ │ ├── _version.py
│ │ │ ├── connection.py
│ │ │ ├── connectionpool.py
│ │ │ ├── contrib
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ ├── emscripten
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── fetch.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── emscripten_fetch_worker.js
│ │ │ │ │ ├── fetch.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ └── response.py
│ │ │ │ ├── pyopenssl.py
│ │ │ │ └── socks.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── filepost.py
│ │ │ ├── http2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ └── probe.cpython-312.pyc
│ │ │ │ ├── connection.py
│ │ │ │ └── probe.py
│ │ │ ├── poolmanager.py
│ │ │ ├── py.typed
│ │ │ ├── response.py
│ │ │ └── util
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ └── wait.cpython-312.pyc
│ │ │ ├── connection.py
│ │ │ ├── proxy.py
│ │ │ ├── request.py
│ │ │ ├── response.py
│ │ │ ├── retry.py
│ │ │ ├── ssl_.py
│ │ │ ├── ssl_match_hostname.py
│ │ │ ├── ssltransport.py
│ │ │ ├── timeout.py
│ │ │ ├── url.py
│ │ │ ├── util.py
│ │ │ └── wait.py
│ │ ├── urllib3-2.2.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── useragent
│ │ │ ├── __init__.py
│ │ │ ├── __init__.pyc
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── resources
│ │ │ │ └── user_agent_data.json
│ │ │ └── test
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── test_additional_os.json
│ │ │ ├── test_browser.json
│ │ │ ├── test_device.json
│ │ │ ├── test_firefox.json
│ │ │ ├── test_os.json
│ │ │ └── test_pgts_browser.json
│ │ ├── useragent-0.1.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── werkzeug
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _internal.cpython-312.pyc
│ │ │ │ ├── _reloader.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formparser.cpython-312.pyc
│ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ ├── local.cpython-312.pyc
│ │ │ │ ├── security.cpython-312.pyc
│ │ │ │ ├── serving.cpython-312.pyc
│ │ │ │ ├── test.cpython-312.pyc
│ │ │ │ ├── testapp.cpython-312.pyc
│ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ ├── user_agent.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── wsgi.cpython-312.pyc
│ │ │ ├── _internal.py
│ │ │ ├── _reloader.py
│ │ │ ├── datastructures
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── accept.cpython-312.pyc
│ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ ├── cache_control.cpython-312.pyc
│ │ │ │ │ ├── csp.cpython-312.pyc
│ │ │ │ │ ├── etag.cpython-312.pyc
│ │ │ │ │ ├── file_storage.cpython-312.pyc
│ │ │ │ │ ├── headers.cpython-312.pyc
│ │ │ │ │ ├── mixins.cpython-312.pyc
│ │ │ │ │ ├── range.cpython-312.pyc
│ │ │ │ │ └── structures.cpython-312.pyc
│ │ │ │ ├── accept.py
│ │ │ │ ├── accept.pyi
│ │ │ │ ├── auth.py
│ │ │ │ ├── cache_control.py
│ │ │ │ ├── cache_control.pyi
│ │ │ │ ├── csp.py
│ │ │ │ ├── csp.pyi
│ │ │ │ ├── etag.py
│ │ │ │ ├── etag.pyi
│ │ │ │ ├── file_storage.py
│ │ │ │ ├── file_storage.pyi
│ │ │ │ ├── headers.py
│ │ │ │ ├── headers.pyi
│ │ │ │ ├── mixins.py
│ │ │ │ ├── mixins.pyi
│ │ │ │ ├── range.py
│ │ │ │ ├── range.pyi
│ │ │ │ ├── structures.py
│ │ │ │ └── structures.pyi
│ │ │ ├── debug
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ └── tbtools.cpython-312.pyc
│ │ │ │ ├── console.py
│ │ │ │ ├── repr.py
│ │ │ │ ├── shared
│ │ │ │ │ ├── console.png
│ │ │ │ │ ├── debugger.js
│ │ │ │ │ ├── ICON_LICENSE.md
│ │ │ │ │ ├── less.png
│ │ │ │ │ ├── more.png
│ │ │ │ │ └── style.css
│ │ │ │ └── tbtools.py
│ │ │ ├── exceptions.py
│ │ │ ├── formparser.py
│ │ │ ├── http.py
│ │ │ ├── local.py
│ │ │ ├── middleware
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── dispatcher.cpython-312.pyc
│ │ │ │ │ ├── http_proxy.cpython-312.pyc
│ │ │ │ │ ├── lint.cpython-312.pyc
│ │ │ │ │ ├── profiler.cpython-312.pyc
│ │ │ │ │ ├── proxy_fix.cpython-312.pyc
│ │ │ │ │ └── shared_data.cpython-312.pyc
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── http_proxy.py
│ │ │ │ ├── lint.py
│ │ │ │ ├── profiler.py
│ │ │ │ ├── proxy_fix.py
│ │ │ │ └── shared_data.py
│ │ │ ├── py.typed
│ │ │ ├── routing
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── converters.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── map.cpython-312.pyc
│ │ │ │ │ ├── matcher.cpython-312.pyc
│ │ │ │ │ └── rules.cpython-312.pyc
│ │ │ │ ├── converters.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── map.py
│ │ │ │ ├── matcher.py
│ │ │ │ └── rules.py
│ │ │ ├── sansio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ │ ├── multipart.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ ├── http.py
│ │ │ │ ├── multipart.py
│ │ │ │ ├── request.py
│ │ │ │ ├── response.py
│ │ │ │ └── utils.py
│ │ │ ├── security.py
│ │ │ ├── serving.py
│ │ │ ├── test.py
│ │ │ ├── testapp.py
│ │ │ ├── urls.py
│ │ │ ├── user_agent.py
│ │ │ ├── utils.py
│ │ │ ├── wrappers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ ├── request.py
│ │ │ │ └── response.py
│ │ │ └── wsgi.py
│ │ └── werkzeug-3.0.4.dist-info
│ │ ├── INSTALLER
│ │ ├── LICENSE.txt
│ │ ├── METADATA
│ │ ├── RECORD
│ │ └── WHEEL
│ ├── pyvenv.cfg
│ ├── static
│ │ └── styles.css
│ ├── templates
│ │ └── index.html
│ └── test.py
├── cline_config.json
├── mcp_server.py
├── README.md
├── search_results.json
├── settings.json
└── test_files
├── text1.txt
└── text2.txt
```
# Files
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import email.feedparser
4 | import email.header
5 | import email.message
6 | import email.parser
7 | import email.policy
8 | import typing
9 | from typing import (
10 | Any,
11 | Callable,
12 | Generic,
13 | Literal,
14 | TypedDict,
15 | cast,
16 | )
17 |
18 | from . import requirements, specifiers, utils
19 | from . import version as version_module
20 |
21 | T = typing.TypeVar("T")
22 |
23 |
24 | try:
25 | ExceptionGroup
26 | except NameError: # pragma: no cover
27 |
28 | class ExceptionGroup(Exception):
29 | """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
30 |
31 | If :external:exc:`ExceptionGroup` is already defined by Python itself,
32 | that version is used instead.
33 | """
34 |
35 | message: str
36 | exceptions: list[Exception]
37 |
38 | def __init__(self, message: str, exceptions: list[Exception]) -> None:
39 | self.message = message
40 | self.exceptions = exceptions
41 |
42 | def __repr__(self) -> str:
43 | return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})"
44 |
45 | else: # pragma: no cover
46 | ExceptionGroup = ExceptionGroup
47 |
48 |
49 | class InvalidMetadata(ValueError):
50 | """A metadata field contains invalid data."""
51 |
52 | field: str
53 | """The name of the field that contains invalid data."""
54 |
55 | def __init__(self, field: str, message: str) -> None:
56 | self.field = field
57 | super().__init__(message)
58 |
59 |
60 | # The RawMetadata class attempts to make as few assumptions about the underlying
61 | # serialization formats as possible. The idea is that as long as a serialization
62 | # formats offer some very basic primitives in *some* way then we can support
63 | # serializing to and from that format.
64 | class RawMetadata(TypedDict, total=False):
65 | """A dictionary of raw core metadata.
66 |
67 | Each field in core metadata maps to a key of this dictionary (when data is
68 | provided). The key is lower-case and underscores are used instead of dashes
69 | compared to the equivalent core metadata field. Any core metadata field that
70 | can be specified multiple times or can hold multiple values in a single
71 | field have a key with a plural name. See :class:`Metadata` whose attributes
72 | match the keys of this dictionary.
73 |
74 | Core metadata fields that can be specified multiple times are stored as a
75 | list or dict depending on which is appropriate for the field. Any fields
76 | which hold multiple values in a single field are stored as a list.
77 |
78 | """
79 |
80 | # Metadata 1.0 - PEP 241
81 | metadata_version: str
82 | name: str
83 | version: str
84 | platforms: list[str]
85 | summary: str
86 | description: str
87 | keywords: list[str]
88 | home_page: str
89 | author: str
90 | author_email: str
91 | license: str
92 |
93 | # Metadata 1.1 - PEP 314
94 | supported_platforms: list[str]
95 | download_url: str
96 | classifiers: list[str]
97 | requires: list[str]
98 | provides: list[str]
99 | obsoletes: list[str]
100 |
101 | # Metadata 1.2 - PEP 345
102 | maintainer: str
103 | maintainer_email: str
104 | requires_dist: list[str]
105 | provides_dist: list[str]
106 | obsoletes_dist: list[str]
107 | requires_python: str
108 | requires_external: list[str]
109 | project_urls: dict[str, str]
110 |
111 | # Metadata 2.0
112 | # PEP 426 attempted to completely revamp the metadata format
113 | # but got stuck without ever being able to build consensus on
114 | # it and ultimately ended up withdrawn.
115 | #
116 | # However, a number of tools had started emitting METADATA with
117 | # `2.0` Metadata-Version, so for historical reasons, this version
118 | # was skipped.
119 |
120 | # Metadata 2.1 - PEP 566
121 | description_content_type: str
122 | provides_extra: list[str]
123 |
124 | # Metadata 2.2 - PEP 643
125 | dynamic: list[str]
126 |
127 | # Metadata 2.3 - PEP 685
128 | # No new fields were added in PEP 685, just some edge case were
129 | # tightened up to provide better interoptability.
130 |
131 |
132 | _STRING_FIELDS = {
133 | "author",
134 | "author_email",
135 | "description",
136 | "description_content_type",
137 | "download_url",
138 | "home_page",
139 | "license",
140 | "maintainer",
141 | "maintainer_email",
142 | "metadata_version",
143 | "name",
144 | "requires_python",
145 | "summary",
146 | "version",
147 | }
148 |
149 | _LIST_FIELDS = {
150 | "classifiers",
151 | "dynamic",
152 | "obsoletes",
153 | "obsoletes_dist",
154 | "platforms",
155 | "provides",
156 | "provides_dist",
157 | "provides_extra",
158 | "requires",
159 | "requires_dist",
160 | "requires_external",
161 | "supported_platforms",
162 | }
163 |
164 | _DICT_FIELDS = {
165 | "project_urls",
166 | }
167 |
168 |
169 | def _parse_keywords(data: str) -> list[str]:
170 | """Split a string of comma-separate keyboards into a list of keywords."""
171 | return [k.strip() for k in data.split(",")]
172 |
173 |
174 | def _parse_project_urls(data: list[str]) -> dict[str, str]:
175 | """Parse a list of label/URL string pairings separated by a comma."""
176 | urls = {}
177 | for pair in data:
178 | # Our logic is slightly tricky here as we want to try and do
179 | # *something* reasonable with malformed data.
180 | #
181 | # The main thing that we have to worry about, is data that does
182 | # not have a ',' at all to split the label from the Value. There
183 | # isn't a singular right answer here, and we will fail validation
184 | # later on (if the caller is validating) so it doesn't *really*
185 | # matter, but since the missing value has to be an empty str
186 | # and our return value is dict[str, str], if we let the key
187 | # be the missing value, then they'd have multiple '' values that
188 | # overwrite each other in a accumulating dict.
189 | #
190 | # The other potentional issue is that it's possible to have the
191 | # same label multiple times in the metadata, with no solid "right"
192 | # answer with what to do in that case. As such, we'll do the only
193 | # thing we can, which is treat the field as unparseable and add it
194 | # to our list of unparsed fields.
195 | parts = [p.strip() for p in pair.split(",", 1)]
196 | parts.extend([""] * (max(0, 2 - len(parts)))) # Ensure 2 items
197 |
198 | # TODO: The spec doesn't say anything about if the keys should be
199 | # considered case sensitive or not... logically they should
200 | # be case-preserving and case-insensitive, but doing that
201 | # would open up more cases where we might have duplicate
202 | # entries.
203 | label, url = parts
204 | if label in urls:
205 | # The label already exists in our set of urls, so this field
206 | # is unparseable, and we can just add the whole thing to our
207 | # unparseable data and stop processing it.
208 | raise KeyError("duplicate labels in project urls")
209 | urls[label] = url
210 |
211 | return urls
212 |
213 |
214 | def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
215 | """Get the body of the message."""
216 | # If our source is a str, then our caller has managed encodings for us,
217 | # and we don't need to deal with it.
218 | if isinstance(source, str):
219 | payload: str = msg.get_payload()
220 | return payload
221 | # If our source is a bytes, then we're managing the encoding and we need
222 | # to deal with it.
223 | else:
224 | bpayload: bytes = msg.get_payload(decode=True)
225 | try:
226 | return bpayload.decode("utf8", "strict")
227 | except UnicodeDecodeError:
228 | raise ValueError("payload in an invalid encoding")
229 |
230 |
231 | # The various parse_FORMAT functions here are intended to be as lenient as
232 | # possible in their parsing, while still returning a correctly typed
233 | # RawMetadata.
234 | #
235 | # To aid in this, we also generally want to do as little touching of the
236 | # data as possible, except where there are possibly some historic holdovers
237 | # that make valid data awkward to work with.
238 | #
239 | # While this is a lower level, intermediate format than our ``Metadata``
240 | # class, some light touch ups can make a massive difference in usability.
241 |
242 | # Map METADATA fields to RawMetadata.
243 | _EMAIL_TO_RAW_MAPPING = {
244 | "author": "author",
245 | "author-email": "author_email",
246 | "classifier": "classifiers",
247 | "description": "description",
248 | "description-content-type": "description_content_type",
249 | "download-url": "download_url",
250 | "dynamic": "dynamic",
251 | "home-page": "home_page",
252 | "keywords": "keywords",
253 | "license": "license",
254 | "maintainer": "maintainer",
255 | "maintainer-email": "maintainer_email",
256 | "metadata-version": "metadata_version",
257 | "name": "name",
258 | "obsoletes": "obsoletes",
259 | "obsoletes-dist": "obsoletes_dist",
260 | "platform": "platforms",
261 | "project-url": "project_urls",
262 | "provides": "provides",
263 | "provides-dist": "provides_dist",
264 | "provides-extra": "provides_extra",
265 | "requires": "requires",
266 | "requires-dist": "requires_dist",
267 | "requires-external": "requires_external",
268 | "requires-python": "requires_python",
269 | "summary": "summary",
270 | "supported-platform": "supported_platforms",
271 | "version": "version",
272 | }
273 | _RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()}
274 |
275 |
276 | def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
277 | """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``).
278 |
279 | This function returns a two-item tuple of dicts. The first dict is of
280 | recognized fields from the core metadata specification. Fields that can be
281 | parsed and translated into Python's built-in types are converted
282 | appropriately. All other fields are left as-is. Fields that are allowed to
283 | appear multiple times are stored as lists.
284 |
285 | The second dict contains all other fields from the metadata. This includes
286 | any unrecognized fields. It also includes any fields which are expected to
287 | be parsed into a built-in type but were not formatted appropriately. Finally,
288 | any fields that are expected to appear only once but are repeated are
289 | included in this dict.
290 |
291 | """
292 | raw: dict[str, str | list[str] | dict[str, str]] = {}
293 | unparsed: dict[str, list[str]] = {}
294 |
295 | if isinstance(data, str):
296 | parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data)
297 | else:
298 | parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data)
299 |
300 | # We have to wrap parsed.keys() in a set, because in the case of multiple
301 | # values for a key (a list), the key will appear multiple times in the
302 | # list of keys, but we're avoiding that by using get_all().
303 | for name in frozenset(parsed.keys()):
304 | # Header names in RFC are case insensitive, so we'll normalize to all
305 | # lower case to make comparisons easier.
306 | name = name.lower()
307 |
308 | # We use get_all() here, even for fields that aren't multiple use,
309 | # because otherwise someone could have e.g. two Name fields, and we
310 | # would just silently ignore it rather than doing something about it.
311 | headers = parsed.get_all(name) or []
312 |
313 | # The way the email module works when parsing bytes is that it
314 | # unconditionally decodes the bytes as ascii using the surrogateescape
315 | # handler. When you pull that data back out (such as with get_all() ),
316 | # it looks to see if the str has any surrogate escapes, and if it does
317 | # it wraps it in a Header object instead of returning the string.
318 | #
319 | # As such, we'll look for those Header objects, and fix up the encoding.
320 | value = []
321 | # Flag if we have run into any issues processing the headers, thus
322 | # signalling that the data belongs in 'unparsed'.
323 | valid_encoding = True
324 | for h in headers:
325 | # It's unclear if this can return more types than just a Header or
326 | # a str, so we'll just assert here to make sure.
327 | assert isinstance(h, (email.header.Header, str))
328 |
329 | # If it's a header object, we need to do our little dance to get
330 | # the real data out of it. In cases where there is invalid data
331 | # we're going to end up with mojibake, but there's no obvious, good
332 | # way around that without reimplementing parts of the Header object
333 | # ourselves.
334 | #
335 | # That should be fine since, if mojibacked happens, this key is
336 | # going into the unparsed dict anyways.
337 | if isinstance(h, email.header.Header):
338 | # The Header object stores it's data as chunks, and each chunk
339 | # can be independently encoded, so we'll need to check each
340 | # of them.
341 | chunks: list[tuple[bytes, str | None]] = []
342 | for bin, encoding in email.header.decode_header(h):
343 | try:
344 | bin.decode("utf8", "strict")
345 | except UnicodeDecodeError:
346 | # Enable mojibake.
347 | encoding = "latin1"
348 | valid_encoding = False
349 | else:
350 | encoding = "utf8"
351 | chunks.append((bin, encoding))
352 |
353 | # Turn our chunks back into a Header object, then let that
354 | # Header object do the right thing to turn them into a
355 | # string for us.
356 | value.append(str(email.header.make_header(chunks)))
357 | # This is already a string, so just add it.
358 | else:
359 | value.append(h)
360 |
361 | # We've processed all of our values to get them into a list of str,
362 | # but we may have mojibake data, in which case this is an unparsed
363 | # field.
364 | if not valid_encoding:
365 | unparsed[name] = value
366 | continue
367 |
368 | raw_name = _EMAIL_TO_RAW_MAPPING.get(name)
369 | if raw_name is None:
370 | # This is a bit of a weird situation, we've encountered a key that
371 | # we don't know what it means, so we don't know whether it's meant
372 | # to be a list or not.
373 | #
374 | # Since we can't really tell one way or another, we'll just leave it
375 | # as a list, even though it may be a single item list, because that's
376 | # what makes the most sense for email headers.
377 | unparsed[name] = value
378 | continue
379 |
380 | # If this is one of our string fields, then we'll check to see if our
381 | # value is a list of a single item. If it is then we'll assume that
382 | # it was emitted as a single string, and unwrap the str from inside
383 | # the list.
384 | #
385 | # If it's any other kind of data, then we haven't the faintest clue
386 | # what we should parse it as, and we have to just add it to our list
387 | # of unparsed stuff.
388 | if raw_name in _STRING_FIELDS and len(value) == 1:
389 | raw[raw_name] = value[0]
390 | # If this is one of our list of string fields, then we can just assign
391 | # the value, since email *only* has strings, and our get_all() call
392 | # above ensures that this is a list.
393 | elif raw_name in _LIST_FIELDS:
394 | raw[raw_name] = value
395 | # Special Case: Keywords
396 | # The keywords field is implemented in the metadata spec as a str,
397 | # but it conceptually is a list of strings, and is serialized using
398 | # ", ".join(keywords), so we'll do some light data massaging to turn
399 | # this into what it logically is.
400 | elif raw_name == "keywords" and len(value) == 1:
401 | raw[raw_name] = _parse_keywords(value[0])
402 | # Special Case: Project-URL
403 | # The project urls is implemented in the metadata spec as a list of
404 | # specially-formatted strings that represent a key and a value, which
405 | # is fundamentally a mapping, however the email format doesn't support
406 | # mappings in a sane way, so it was crammed into a list of strings
407 | # instead.
408 | #
409 | # We will do a little light data massaging to turn this into a map as
410 | # it logically should be.
411 | elif raw_name == "project_urls":
412 | try:
413 | raw[raw_name] = _parse_project_urls(value)
414 | except KeyError:
415 | unparsed[name] = value
416 | # Nothing that we've done has managed to parse this, so it'll just
417 | # throw it in our unparseable data and move on.
418 | else:
419 | unparsed[name] = value
420 |
421 | # We need to support getting the Description from the message payload in
422 | # addition to getting it from the the headers. This does mean, though, there
423 | # is the possibility of it being set both ways, in which case we put both
424 | # in 'unparsed' since we don't know which is right.
425 | try:
426 | payload = _get_payload(parsed, data)
427 | except ValueError:
428 | unparsed.setdefault("description", []).append(
429 | parsed.get_payload(decode=isinstance(data, bytes))
430 | )
431 | else:
432 | if payload:
433 | # Check to see if we've already got a description, if so then both
434 | # it, and this body move to unparseable.
435 | if "description" in raw:
436 | description_header = cast(str, raw.pop("description"))
437 | unparsed.setdefault("description", []).extend(
438 | [description_header, payload]
439 | )
440 | elif "description" in unparsed:
441 | unparsed["description"].append(payload)
442 | else:
443 | raw["description"] = payload
444 |
445 | # We need to cast our `raw` to a metadata, because a TypedDict only support
446 | # literal key names, but we're computing our key names on purpose, but the
447 | # way this function is implemented, our `TypedDict` can only have valid key
448 | # names.
449 | return cast(RawMetadata, raw), unparsed
450 |
451 |
452 | _NOT_FOUND = object()
453 |
454 |
455 | # Keep the two values in sync.
456 | _VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"]
457 | _MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"]
458 |
459 | _REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"])
460 |
461 |
462 | class _Validator(Generic[T]):
463 | """Validate a metadata field.
464 |
465 | All _process_*() methods correspond to a core metadata field. The method is
466 | called with the field's raw value. If the raw value is valid it is returned
467 | in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field).
468 | If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause
469 | as appropriate).
470 | """
471 |
472 | name: str
473 | raw_name: str
474 | added: _MetadataVersion
475 |
476 | def __init__(
477 | self,
478 | *,
479 | added: _MetadataVersion = "1.0",
480 | ) -> None:
481 | self.added = added
482 |
483 | def __set_name__(self, _owner: Metadata, name: str) -> None:
484 | self.name = name
485 | self.raw_name = _RAW_TO_EMAIL_MAPPING[name]
486 |
487 | def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T:
488 | # With Python 3.8, the caching can be replaced with functools.cached_property().
489 | # No need to check the cache as attribute lookup will resolve into the
490 | # instance's __dict__ before __get__ is called.
491 | cache = instance.__dict__
492 | value = instance._raw.get(self.name)
493 |
494 | # To make the _process_* methods easier, we'll check if the value is None
495 | # and if this field is NOT a required attribute, and if both of those
496 | # things are true, we'll skip the the converter. This will mean that the
497 | # converters never have to deal with the None union.
498 | if self.name in _REQUIRED_ATTRS or value is not None:
499 | try:
500 | converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}")
501 | except AttributeError:
502 | pass
503 | else:
504 | value = converter(value)
505 |
506 | cache[self.name] = value
507 | try:
508 | del instance._raw[self.name] # type: ignore[misc]
509 | except KeyError:
510 | pass
511 |
512 | return cast(T, value)
513 |
514 | def _invalid_metadata(
515 | self, msg: str, cause: Exception | None = None
516 | ) -> InvalidMetadata:
517 | exc = InvalidMetadata(
518 | self.raw_name, msg.format_map({"field": repr(self.raw_name)})
519 | )
520 | exc.__cause__ = cause
521 | return exc
522 |
523 | def _process_metadata_version(self, value: str) -> _MetadataVersion:
524 | # Implicitly makes Metadata-Version required.
525 | if value not in _VALID_METADATA_VERSIONS:
526 | raise self._invalid_metadata(f"{value!r} is not a valid metadata version")
527 | return cast(_MetadataVersion, value)
528 |
529 | def _process_name(self, value: str) -> str:
530 | if not value:
531 | raise self._invalid_metadata("{field} is a required field")
532 | # Validate the name as a side-effect.
533 | try:
534 | utils.canonicalize_name(value, validate=True)
535 | except utils.InvalidName as exc:
536 | raise self._invalid_metadata(
537 | f"{value!r} is invalid for {{field}}", cause=exc
538 | )
539 | else:
540 | return value
541 |
542 | def _process_version(self, value: str) -> version_module.Version:
543 | if not value:
544 | raise self._invalid_metadata("{field} is a required field")
545 | try:
546 | return version_module.parse(value)
547 | except version_module.InvalidVersion as exc:
548 | raise self._invalid_metadata(
549 | f"{value!r} is invalid for {{field}}", cause=exc
550 | )
551 |
552 | def _process_summary(self, value: str) -> str:
553 | """Check the field contains no newlines."""
554 | if "\n" in value:
555 | raise self._invalid_metadata("{field} must be a single line")
556 | return value
557 |
558 | def _process_description_content_type(self, value: str) -> str:
559 | content_types = {"text/plain", "text/x-rst", "text/markdown"}
560 | message = email.message.EmailMessage()
561 | message["content-type"] = value
562 |
563 | content_type, parameters = (
564 | # Defaults to `text/plain` if parsing failed.
565 | message.get_content_type().lower(),
566 | message["content-type"].params,
567 | )
568 | # Check if content-type is valid or defaulted to `text/plain` and thus was
569 | # not parseable.
570 | if content_type not in content_types or content_type not in value.lower():
571 | raise self._invalid_metadata(
572 | f"{{field}} must be one of {list(content_types)}, not {value!r}"
573 | )
574 |
575 | charset = parameters.get("charset", "UTF-8")
576 | if charset != "UTF-8":
577 | raise self._invalid_metadata(
578 | f"{{field}} can only specify the UTF-8 charset, not {list(charset)}"
579 | )
580 |
581 | markdown_variants = {"GFM", "CommonMark"}
582 | variant = parameters.get("variant", "GFM") # Use an acceptable default.
583 | if content_type == "text/markdown" and variant not in markdown_variants:
584 | raise self._invalid_metadata(
585 | f"valid Markdown variants for {{field}} are {list(markdown_variants)}, "
586 | f"not {variant!r}",
587 | )
588 | return value
589 |
590 | def _process_dynamic(self, value: list[str]) -> list[str]:
591 | for dynamic_field in map(str.lower, value):
592 | if dynamic_field in {"name", "version", "metadata-version"}:
593 | raise self._invalid_metadata(
594 | f"{value!r} is not allowed as a dynamic field"
595 | )
596 | elif dynamic_field not in _EMAIL_TO_RAW_MAPPING:
597 | raise self._invalid_metadata(f"{value!r} is not a valid dynamic field")
598 | return list(map(str.lower, value))
599 |
600 | def _process_provides_extra(
601 | self,
602 | value: list[str],
603 | ) -> list[utils.NormalizedName]:
604 | normalized_names = []
605 | try:
606 | for name in value:
607 | normalized_names.append(utils.canonicalize_name(name, validate=True))
608 | except utils.InvalidName as exc:
609 | raise self._invalid_metadata(
610 | f"{name!r} is invalid for {{field}}", cause=exc
611 | )
612 | else:
613 | return normalized_names
614 |
615 | def _process_requires_python(self, value: str) -> specifiers.SpecifierSet:
616 | try:
617 | return specifiers.SpecifierSet(value)
618 | except specifiers.InvalidSpecifier as exc:
619 | raise self._invalid_metadata(
620 | f"{value!r} is invalid for {{field}}", cause=exc
621 | )
622 |
623 | def _process_requires_dist(
624 | self,
625 | value: list[str],
626 | ) -> list[requirements.Requirement]:
627 | reqs = []
628 | try:
629 | for req in value:
630 | reqs.append(requirements.Requirement(req))
631 | except requirements.InvalidRequirement as exc:
632 | raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc)
633 | else:
634 | return reqs
635 |
636 |
637 | class Metadata:
638 | """Representation of distribution metadata.
639 |
640 | Compared to :class:`RawMetadata`, this class provides objects representing
641 | metadata fields instead of only using built-in types. Any invalid metadata
642 | will cause :exc:`InvalidMetadata` to be raised (with a
643 | :py:attr:`~BaseException.__cause__` attribute as appropriate).
644 | """
645 |
646 | _raw: RawMetadata
647 |
648 | @classmethod
649 | def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata:
650 | """Create an instance from :class:`RawMetadata`.
651 |
652 | If *validate* is true, all metadata will be validated. All exceptions
653 | related to validation will be gathered and raised as an :class:`ExceptionGroup`.
654 | """
655 | ins = cls()
656 | ins._raw = data.copy() # Mutations occur due to caching enriched values.
657 |
658 | if validate:
659 | exceptions: list[Exception] = []
660 | try:
661 | metadata_version = ins.metadata_version
662 | metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
663 | except InvalidMetadata as metadata_version_exc:
664 | exceptions.append(metadata_version_exc)
665 | metadata_version = None
666 |
667 | # Make sure to check for the fields that are present, the required
668 | # fields (so their absence can be reported).
669 | fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS
670 | # Remove fields that have already been checked.
671 | fields_to_check -= {"metadata_version"}
672 |
673 | for key in fields_to_check:
674 | try:
675 | if metadata_version:
676 | # Can't use getattr() as that triggers descriptor protocol which
677 | # will fail due to no value for the instance argument.
678 | try:
679 | field_metadata_version = cls.__dict__[key].added
680 | except KeyError:
681 | exc = InvalidMetadata(key, f"unrecognized field: {key!r}")
682 | exceptions.append(exc)
683 | continue
684 | field_age = _VALID_METADATA_VERSIONS.index(
685 | field_metadata_version
686 | )
687 | if field_age > metadata_age:
688 | field = _RAW_TO_EMAIL_MAPPING[key]
689 | exc = InvalidMetadata(
690 | field,
691 | "{field} introduced in metadata version "
692 | "{field_metadata_version}, not {metadata_version}",
693 | )
694 | exceptions.append(exc)
695 | continue
696 | getattr(ins, key)
697 | except InvalidMetadata as exc:
698 | exceptions.append(exc)
699 |
700 | if exceptions:
701 | raise ExceptionGroup("invalid metadata", exceptions)
702 |
703 | return ins
704 |
705 | @classmethod
706 | def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
707 | """Parse metadata from email headers.
708 |
709 | If *validate* is true, the metadata will be validated. All exceptions
710 | related to validation will be gathered and raised as an :class:`ExceptionGroup`.
711 | """
712 | raw, unparsed = parse_email(data)
713 |
714 | if validate:
715 | exceptions: list[Exception] = []
716 | for unparsed_key in unparsed:
717 | if unparsed_key in _EMAIL_TO_RAW_MAPPING:
718 | message = f"{unparsed_key!r} has invalid data"
719 | else:
720 | message = f"unrecognized field: {unparsed_key!r}"
721 | exceptions.append(InvalidMetadata(unparsed_key, message))
722 |
723 | if exceptions:
724 | raise ExceptionGroup("unparsed", exceptions)
725 |
726 | try:
727 | return cls.from_raw(raw, validate=validate)
728 | except ExceptionGroup as exc_group:
729 | raise ExceptionGroup(
730 | "invalid or unparsed metadata", exc_group.exceptions
731 | ) from None
732 |
733 | metadata_version: _Validator[_MetadataVersion] = _Validator()
734 | """:external:ref:`core-metadata-metadata-version`
735 | (required; validated to be a valid metadata version)"""
736 | name: _Validator[str] = _Validator()
737 | """:external:ref:`core-metadata-name`
738 | (required; validated using :func:`~packaging.utils.canonicalize_name` and its
739 | *validate* parameter)"""
740 | version: _Validator[version_module.Version] = _Validator()
741 | """:external:ref:`core-metadata-version` (required)"""
742 | dynamic: _Validator[list[str] | None] = _Validator(
743 | added="2.2",
744 | )
745 | """:external:ref:`core-metadata-dynamic`
746 | (validated against core metadata field names and lowercased)"""
747 | platforms: _Validator[list[str] | None] = _Validator()
748 | """:external:ref:`core-metadata-platform`"""
749 | supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1")
750 | """:external:ref:`core-metadata-supported-platform`"""
751 | summary: _Validator[str | None] = _Validator()
752 | """:external:ref:`core-metadata-summary` (validated to contain no newlines)"""
753 | description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body
754 | """:external:ref:`core-metadata-description`"""
755 | description_content_type: _Validator[str | None] = _Validator(added="2.1")
756 | """:external:ref:`core-metadata-description-content-type` (validated)"""
757 | keywords: _Validator[list[str] | None] = _Validator()
758 | """:external:ref:`core-metadata-keywords`"""
759 | home_page: _Validator[str | None] = _Validator()
760 | """:external:ref:`core-metadata-home-page`"""
761 | download_url: _Validator[str | None] = _Validator(added="1.1")
762 | """:external:ref:`core-metadata-download-url`"""
763 | author: _Validator[str | None] = _Validator()
764 | """:external:ref:`core-metadata-author`"""
765 | author_email: _Validator[str | None] = _Validator()
766 | """:external:ref:`core-metadata-author-email`"""
767 | maintainer: _Validator[str | None] = _Validator(added="1.2")
768 | """:external:ref:`core-metadata-maintainer`"""
769 | maintainer_email: _Validator[str | None] = _Validator(added="1.2")
770 | """:external:ref:`core-metadata-maintainer-email`"""
771 | license: _Validator[str | None] = _Validator()
772 | """:external:ref:`core-metadata-license`"""
773 | classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
774 | """:external:ref:`core-metadata-classifier`"""
775 | requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
776 | added="1.2"
777 | )
778 | """:external:ref:`core-metadata-requires-dist`"""
779 | requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator(
780 | added="1.2"
781 | )
782 | """:external:ref:`core-metadata-requires-python`"""
783 | # Because `Requires-External` allows for non-PEP 440 version specifiers, we
784 | # don't do any processing on the values.
785 | requires_external: _Validator[list[str] | None] = _Validator(added="1.2")
786 | """:external:ref:`core-metadata-requires-external`"""
787 | project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2")
788 | """:external:ref:`core-metadata-project-url`"""
789 | # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
790 | # regardless of metadata version.
791 | provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
792 | added="2.1",
793 | )
794 | """:external:ref:`core-metadata-provides-extra`"""
795 | provides_dist: _Validator[list[str] | None] = _Validator(added="1.2")
796 | """:external:ref:`core-metadata-provides-dist`"""
797 | obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2")
798 | """:external:ref:`core-metadata-obsoletes-dist`"""
799 | requires: _Validator[list[str] | None] = _Validator(added="1.1")
800 | """``Requires`` (deprecated)"""
801 | provides: _Validator[list[str] | None] = _Validator(added="1.1")
802 | """``Provides`` (deprecated)"""
803 | obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
804 | """``Obsoletes`` (deprecated)"""
805 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py:
--------------------------------------------------------------------------------
```python
1 | import contextlib
2 | import functools
3 | import logging
4 | from typing import (
5 | TYPE_CHECKING,
6 | Callable,
7 | Dict,
8 | FrozenSet,
9 | Iterable,
10 | Iterator,
11 | List,
12 | Mapping,
13 | NamedTuple,
14 | Optional,
15 | Protocol,
16 | Sequence,
17 | Set,
18 | Tuple,
19 | TypeVar,
20 | cast,
21 | )
22 |
23 | from pip._vendor.packaging.requirements import InvalidRequirement
24 | from pip._vendor.packaging.specifiers import SpecifierSet
25 | from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
26 | from pip._vendor.packaging.version import Version
27 | from pip._vendor.resolvelib import ResolutionImpossible
28 |
29 | from pip._internal.cache import CacheEntry, WheelCache
30 | from pip._internal.exceptions import (
31 | DistributionNotFound,
32 | InstallationError,
33 | MetadataInconsistent,
34 | MetadataInvalid,
35 | UnsupportedPythonVersion,
36 | UnsupportedWheel,
37 | )
38 | from pip._internal.index.package_finder import PackageFinder
39 | from pip._internal.metadata import BaseDistribution, get_default_environment
40 | from pip._internal.models.link import Link
41 | from pip._internal.models.wheel import Wheel
42 | from pip._internal.operations.prepare import RequirementPreparer
43 | from pip._internal.req.constructors import (
44 | install_req_drop_extras,
45 | install_req_from_link_and_ireq,
46 | )
47 | from pip._internal.req.req_install import (
48 | InstallRequirement,
49 | check_invalid_constraint_type,
50 | )
51 | from pip._internal.resolution.base import InstallRequirementProvider
52 | from pip._internal.utils.compatibility_tags import get_supported
53 | from pip._internal.utils.hashes import Hashes
54 | from pip._internal.utils.packaging import get_requirement
55 | from pip._internal.utils.virtualenv import running_under_virtualenv
56 |
57 | from .base import Candidate, Constraint, Requirement
58 | from .candidates import (
59 | AlreadyInstalledCandidate,
60 | BaseCandidate,
61 | EditableCandidate,
62 | ExtrasCandidate,
63 | LinkCandidate,
64 | RequiresPythonCandidate,
65 | as_base_candidate,
66 | )
67 | from .found_candidates import FoundCandidates, IndexCandidateInfo
68 | from .requirements import (
69 | ExplicitRequirement,
70 | RequiresPythonRequirement,
71 | SpecifierRequirement,
72 | SpecifierWithoutExtrasRequirement,
73 | UnsatisfiableRequirement,
74 | )
75 |
76 | if TYPE_CHECKING:
77 |
78 | class ConflictCause(Protocol):
79 | requirement: RequiresPythonRequirement
80 | parent: Candidate
81 |
82 |
83 | logger = logging.getLogger(__name__)
84 |
85 | C = TypeVar("C")
86 | Cache = Dict[Link, C]
87 |
88 |
89 | class CollectedRootRequirements(NamedTuple):
90 | requirements: List[Requirement]
91 | constraints: Dict[str, Constraint]
92 | user_requested: Dict[str, int]
93 |
94 |
95 | class Factory:
96 | def __init__(
97 | self,
98 | finder: PackageFinder,
99 | preparer: RequirementPreparer,
100 | make_install_req: InstallRequirementProvider,
101 | wheel_cache: Optional[WheelCache],
102 | use_user_site: bool,
103 | force_reinstall: bool,
104 | ignore_installed: bool,
105 | ignore_requires_python: bool,
106 | py_version_info: Optional[Tuple[int, ...]] = None,
107 | ) -> None:
108 | self._finder = finder
109 | self.preparer = preparer
110 | self._wheel_cache = wheel_cache
111 | self._python_candidate = RequiresPythonCandidate(py_version_info)
112 | self._make_install_req_from_spec = make_install_req
113 | self._use_user_site = use_user_site
114 | self._force_reinstall = force_reinstall
115 | self._ignore_requires_python = ignore_requires_python
116 |
117 | self._build_failures: Cache[InstallationError] = {}
118 | self._link_candidate_cache: Cache[LinkCandidate] = {}
119 | self._editable_candidate_cache: Cache[EditableCandidate] = {}
120 | self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {}
121 | self._extras_candidate_cache: Dict[
122 | Tuple[int, FrozenSet[NormalizedName]], ExtrasCandidate
123 | ] = {}
124 | self._supported_tags_cache = get_supported()
125 |
126 | if not ignore_installed:
127 | env = get_default_environment()
128 | self._installed_dists = {
129 | dist.canonical_name: dist
130 | for dist in env.iter_installed_distributions(local_only=False)
131 | }
132 | else:
133 | self._installed_dists = {}
134 |
135 | @property
136 | def force_reinstall(self) -> bool:
137 | return self._force_reinstall
138 |
139 | def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
140 | if not link.is_wheel:
141 | return
142 | wheel = Wheel(link.filename)
143 | if wheel.supported(self._finder.target_python.get_unsorted_tags()):
144 | return
145 | msg = f"{link.filename} is not a supported wheel on this platform."
146 | raise UnsupportedWheel(msg)
147 |
148 | def _make_extras_candidate(
149 | self,
150 | base: BaseCandidate,
151 | extras: FrozenSet[str],
152 | *,
153 | comes_from: Optional[InstallRequirement] = None,
154 | ) -> ExtrasCandidate:
155 | cache_key = (id(base), frozenset(canonicalize_name(e) for e in extras))
156 | try:
157 | candidate = self._extras_candidate_cache[cache_key]
158 | except KeyError:
159 | candidate = ExtrasCandidate(base, extras, comes_from=comes_from)
160 | self._extras_candidate_cache[cache_key] = candidate
161 | return candidate
162 |
163 | def _make_candidate_from_dist(
164 | self,
165 | dist: BaseDistribution,
166 | extras: FrozenSet[str],
167 | template: InstallRequirement,
168 | ) -> Candidate:
169 | try:
170 | base = self._installed_candidate_cache[dist.canonical_name]
171 | except KeyError:
172 | base = AlreadyInstalledCandidate(dist, template, factory=self)
173 | self._installed_candidate_cache[dist.canonical_name] = base
174 | if not extras:
175 | return base
176 | return self._make_extras_candidate(base, extras, comes_from=template)
177 |
178 | def _make_candidate_from_link(
179 | self,
180 | link: Link,
181 | extras: FrozenSet[str],
182 | template: InstallRequirement,
183 | name: Optional[NormalizedName],
184 | version: Optional[Version],
185 | ) -> Optional[Candidate]:
186 | base: Optional[BaseCandidate] = self._make_base_candidate_from_link(
187 | link, template, name, version
188 | )
189 | if not extras or base is None:
190 | return base
191 | return self._make_extras_candidate(base, extras, comes_from=template)
192 |
193 | def _make_base_candidate_from_link(
194 | self,
195 | link: Link,
196 | template: InstallRequirement,
197 | name: Optional[NormalizedName],
198 | version: Optional[Version],
199 | ) -> Optional[BaseCandidate]:
200 | # TODO: Check already installed candidate, and use it if the link and
201 | # editable flag match.
202 |
203 | if link in self._build_failures:
204 | # We already tried this candidate before, and it does not build.
205 | # Don't bother trying again.
206 | return None
207 |
208 | if template.editable:
209 | if link not in self._editable_candidate_cache:
210 | try:
211 | self._editable_candidate_cache[link] = EditableCandidate(
212 | link,
213 | template,
214 | factory=self,
215 | name=name,
216 | version=version,
217 | )
218 | except (MetadataInconsistent, MetadataInvalid) as e:
219 | logger.info(
220 | "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
221 | link,
222 | e,
223 | extra={"markup": True},
224 | )
225 | self._build_failures[link] = e
226 | return None
227 |
228 | return self._editable_candidate_cache[link]
229 | else:
230 | if link not in self._link_candidate_cache:
231 | try:
232 | self._link_candidate_cache[link] = LinkCandidate(
233 | link,
234 | template,
235 | factory=self,
236 | name=name,
237 | version=version,
238 | )
239 | except MetadataInconsistent as e:
240 | logger.info(
241 | "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
242 | link,
243 | e,
244 | extra={"markup": True},
245 | )
246 | self._build_failures[link] = e
247 | return None
248 | return self._link_candidate_cache[link]
249 |
250 | def _iter_found_candidates(
251 | self,
252 | ireqs: Sequence[InstallRequirement],
253 | specifier: SpecifierSet,
254 | hashes: Hashes,
255 | prefers_installed: bool,
256 | incompatible_ids: Set[int],
257 | ) -> Iterable[Candidate]:
258 | if not ireqs:
259 | return ()
260 |
261 | # The InstallRequirement implementation requires us to give it a
262 | # "template". Here we just choose the first requirement to represent
263 | # all of them.
264 | # Hopefully the Project model can correct this mismatch in the future.
265 | template = ireqs[0]
266 | assert template.req, "Candidates found on index must be PEP 508"
267 | name = canonicalize_name(template.req.name)
268 |
269 | extras: FrozenSet[str] = frozenset()
270 | for ireq in ireqs:
271 | assert ireq.req, "Candidates found on index must be PEP 508"
272 | specifier &= ireq.req.specifier
273 | hashes &= ireq.hashes(trust_internet=False)
274 | extras |= frozenset(ireq.extras)
275 |
276 | def _get_installed_candidate() -> Optional[Candidate]:
277 | """Get the candidate for the currently-installed version."""
278 | # If --force-reinstall is set, we want the version from the index
279 | # instead, so we "pretend" there is nothing installed.
280 | if self._force_reinstall:
281 | return None
282 | try:
283 | installed_dist = self._installed_dists[name]
284 | except KeyError:
285 | return None
286 | # Don't use the installed distribution if its version does not fit
287 | # the current dependency graph.
288 | if not specifier.contains(installed_dist.version, prereleases=True):
289 | return None
290 | candidate = self._make_candidate_from_dist(
291 | dist=installed_dist,
292 | extras=extras,
293 | template=template,
294 | )
295 | # The candidate is a known incompatibility. Don't use it.
296 | if id(candidate) in incompatible_ids:
297 | return None
298 | return candidate
299 |
300 | def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]:
301 | result = self._finder.find_best_candidate(
302 | project_name=name,
303 | specifier=specifier,
304 | hashes=hashes,
305 | )
306 | icans = list(result.iter_applicable())
307 |
308 | # PEP 592: Yanked releases are ignored unless the specifier
309 | # explicitly pins a version (via '==' or '===') that can be
310 | # solely satisfied by a yanked release.
311 | all_yanked = all(ican.link.is_yanked for ican in icans)
312 |
313 | def is_pinned(specifier: SpecifierSet) -> bool:
314 | for sp in specifier:
315 | if sp.operator == "===":
316 | return True
317 | if sp.operator != "==":
318 | continue
319 | if sp.version.endswith(".*"):
320 | continue
321 | return True
322 | return False
323 |
324 | pinned = is_pinned(specifier)
325 |
326 | # PackageFinder returns earlier versions first, so we reverse.
327 | for ican in reversed(icans):
328 | if not (all_yanked and pinned) and ican.link.is_yanked:
329 | continue
330 | func = functools.partial(
331 | self._make_candidate_from_link,
332 | link=ican.link,
333 | extras=extras,
334 | template=template,
335 | name=name,
336 | version=ican.version,
337 | )
338 | yield ican.version, func
339 |
340 | return FoundCandidates(
341 | iter_index_candidate_infos,
342 | _get_installed_candidate(),
343 | prefers_installed,
344 | incompatible_ids,
345 | )
346 |
347 | def _iter_explicit_candidates_from_base(
348 | self,
349 | base_requirements: Iterable[Requirement],
350 | extras: FrozenSet[str],
351 | ) -> Iterator[Candidate]:
352 | """Produce explicit candidates from the base given an extra-ed package.
353 |
354 | :param base_requirements: Requirements known to the resolver. The
355 | requirements are guaranteed to not have extras.
356 | :param extras: The extras to inject into the explicit requirements'
357 | candidates.
358 | """
359 | for req in base_requirements:
360 | lookup_cand, _ = req.get_candidate_lookup()
361 | if lookup_cand is None: # Not explicit.
362 | continue
363 | # We've stripped extras from the identifier, and should always
364 | # get a BaseCandidate here, unless there's a bug elsewhere.
365 | base_cand = as_base_candidate(lookup_cand)
366 | assert base_cand is not None, "no extras here"
367 | yield self._make_extras_candidate(base_cand, extras)
368 |
369 | def _iter_candidates_from_constraints(
370 | self,
371 | identifier: str,
372 | constraint: Constraint,
373 | template: InstallRequirement,
374 | ) -> Iterator[Candidate]:
375 | """Produce explicit candidates from constraints.
376 |
377 | This creates "fake" InstallRequirement objects that are basically clones
378 | of what "should" be the template, but with original_link set to link.
379 | """
380 | for link in constraint.links:
381 | self._fail_if_link_is_unsupported_wheel(link)
382 | candidate = self._make_base_candidate_from_link(
383 | link,
384 | template=install_req_from_link_and_ireq(link, template),
385 | name=canonicalize_name(identifier),
386 | version=None,
387 | )
388 | if candidate:
389 | yield candidate
390 |
391 | def find_candidates(
392 | self,
393 | identifier: str,
394 | requirements: Mapping[str, Iterable[Requirement]],
395 | incompatibilities: Mapping[str, Iterator[Candidate]],
396 | constraint: Constraint,
397 | prefers_installed: bool,
398 | is_satisfied_by: Callable[[Requirement, Candidate], bool],
399 | ) -> Iterable[Candidate]:
400 | # Collect basic lookup information from the requirements.
401 | explicit_candidates: Set[Candidate] = set()
402 | ireqs: List[InstallRequirement] = []
403 | for req in requirements[identifier]:
404 | cand, ireq = req.get_candidate_lookup()
405 | if cand is not None:
406 | explicit_candidates.add(cand)
407 | if ireq is not None:
408 | ireqs.append(ireq)
409 |
410 | # If the current identifier contains extras, add requires and explicit
411 | # candidates from entries from extra-less identifier.
412 | with contextlib.suppress(InvalidRequirement):
413 | parsed_requirement = get_requirement(identifier)
414 | if parsed_requirement.name != identifier:
415 | explicit_candidates.update(
416 | self._iter_explicit_candidates_from_base(
417 | requirements.get(parsed_requirement.name, ()),
418 | frozenset(parsed_requirement.extras),
419 | ),
420 | )
421 | for req in requirements.get(parsed_requirement.name, []):
422 | _, ireq = req.get_candidate_lookup()
423 | if ireq is not None:
424 | ireqs.append(ireq)
425 |
426 | # Add explicit candidates from constraints. We only do this if there are
427 | # known ireqs, which represent requirements not already explicit. If
428 | # there are no ireqs, we're constraining already-explicit requirements,
429 | # which is handled later when we return the explicit candidates.
430 | if ireqs:
431 | try:
432 | explicit_candidates.update(
433 | self._iter_candidates_from_constraints(
434 | identifier,
435 | constraint,
436 | template=ireqs[0],
437 | ),
438 | )
439 | except UnsupportedWheel:
440 | # If we're constrained to install a wheel incompatible with the
441 | # target architecture, no candidates will ever be valid.
442 | return ()
443 |
444 | # Since we cache all the candidates, incompatibility identification
445 | # can be made quicker by comparing only the id() values.
446 | incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
447 |
448 | # If none of the requirements want an explicit candidate, we can ask
449 | # the finder for candidates.
450 | if not explicit_candidates:
451 | return self._iter_found_candidates(
452 | ireqs,
453 | constraint.specifier,
454 | constraint.hashes,
455 | prefers_installed,
456 | incompat_ids,
457 | )
458 |
459 | return (
460 | c
461 | for c in explicit_candidates
462 | if id(c) not in incompat_ids
463 | and constraint.is_satisfied_by(c)
464 | and all(is_satisfied_by(req, c) for req in requirements[identifier])
465 | )
466 |
467 | def _make_requirements_from_install_req(
468 | self, ireq: InstallRequirement, requested_extras: Iterable[str]
469 | ) -> Iterator[Requirement]:
470 | """
471 | Returns requirement objects associated with the given InstallRequirement. In
472 | most cases this will be a single object but the following special cases exist:
473 | - the InstallRequirement has markers that do not apply -> result is empty
474 | - the InstallRequirement has both a constraint (or link) and extras
475 | -> result is split in two requirement objects: one with the constraint
476 | (or link) and one with the extra. This allows centralized constraint
477 | handling for the base, resulting in fewer candidate rejections.
478 | """
479 | if not ireq.match_markers(requested_extras):
480 | logger.info(
481 | "Ignoring %s: markers '%s' don't match your environment",
482 | ireq.name,
483 | ireq.markers,
484 | )
485 | elif not ireq.link:
486 | if ireq.extras and ireq.req is not None and ireq.req.specifier:
487 | yield SpecifierWithoutExtrasRequirement(ireq)
488 | yield SpecifierRequirement(ireq)
489 | else:
490 | self._fail_if_link_is_unsupported_wheel(ireq.link)
491 | # Always make the link candidate for the base requirement to make it
492 | # available to `find_candidates` for explicit candidate lookup for any
493 | # set of extras.
494 | # The extras are required separately via a second requirement.
495 | cand = self._make_base_candidate_from_link(
496 | ireq.link,
497 | template=install_req_drop_extras(ireq) if ireq.extras else ireq,
498 | name=canonicalize_name(ireq.name) if ireq.name else None,
499 | version=None,
500 | )
501 | if cand is None:
502 | # There's no way we can satisfy a URL requirement if the underlying
503 | # candidate fails to build. An unnamed URL must be user-supplied, so
504 | # we fail eagerly. If the URL is named, an unsatisfiable requirement
505 | # can make the resolver do the right thing, either backtrack (and
506 | # maybe find some other requirement that's buildable) or raise a
507 | # ResolutionImpossible eventually.
508 | if not ireq.name:
509 | raise self._build_failures[ireq.link]
510 | yield UnsatisfiableRequirement(canonicalize_name(ireq.name))
511 | else:
512 | # require the base from the link
513 | yield self.make_requirement_from_candidate(cand)
514 | if ireq.extras:
515 | # require the extras on top of the base candidate
516 | yield self.make_requirement_from_candidate(
517 | self._make_extras_candidate(cand, frozenset(ireq.extras))
518 | )
519 |
520 | def collect_root_requirements(
521 | self, root_ireqs: List[InstallRequirement]
522 | ) -> CollectedRootRequirements:
523 | collected = CollectedRootRequirements([], {}, {})
524 | for i, ireq in enumerate(root_ireqs):
525 | if ireq.constraint:
526 | # Ensure we only accept valid constraints
527 | problem = check_invalid_constraint_type(ireq)
528 | if problem:
529 | raise InstallationError(problem)
530 | if not ireq.match_markers():
531 | continue
532 | assert ireq.name, "Constraint must be named"
533 | name = canonicalize_name(ireq.name)
534 | if name in collected.constraints:
535 | collected.constraints[name] &= ireq
536 | else:
537 | collected.constraints[name] = Constraint.from_ireq(ireq)
538 | else:
539 | reqs = list(
540 | self._make_requirements_from_install_req(
541 | ireq,
542 | requested_extras=(),
543 | )
544 | )
545 | if not reqs:
546 | continue
547 | template = reqs[0]
548 | if ireq.user_supplied and template.name not in collected.user_requested:
549 | collected.user_requested[template.name] = i
550 | collected.requirements.extend(reqs)
551 | # Put requirements with extras at the end of the root requires. This does not
552 | # affect resolvelib's picking preference but it does affect its initial criteria
553 | # population: by putting extras at the end we enable the candidate finder to
554 | # present resolvelib with a smaller set of candidates to resolvelib, already
555 | # taking into account any non-transient constraints on the associated base. This
556 | # means resolvelib will have fewer candidates to visit and reject.
557 | # Python's list sort is stable, meaning relative order is kept for objects with
558 | # the same key.
559 | collected.requirements.sort(key=lambda r: r.name != r.project_name)
560 | return collected
561 |
562 | def make_requirement_from_candidate(
563 | self, candidate: Candidate
564 | ) -> ExplicitRequirement:
565 | return ExplicitRequirement(candidate)
566 |
567 | def make_requirements_from_spec(
568 | self,
569 | specifier: str,
570 | comes_from: Optional[InstallRequirement],
571 | requested_extras: Iterable[str] = (),
572 | ) -> Iterator[Requirement]:
573 | """
574 | Returns requirement objects associated with the given specifier. In most cases
575 | this will be a single object but the following special cases exist:
576 | - the specifier has markers that do not apply -> result is empty
577 | - the specifier has both a constraint and extras -> result is split
578 | in two requirement objects: one with the constraint and one with the
579 | extra. This allows centralized constraint handling for the base,
580 | resulting in fewer candidate rejections.
581 | """
582 | ireq = self._make_install_req_from_spec(specifier, comes_from)
583 | return self._make_requirements_from_install_req(ireq, requested_extras)
584 |
585 | def make_requires_python_requirement(
586 | self,
587 | specifier: SpecifierSet,
588 | ) -> Optional[Requirement]:
589 | if self._ignore_requires_python:
590 | return None
591 | # Don't bother creating a dependency for an empty Requires-Python.
592 | if not str(specifier):
593 | return None
594 | return RequiresPythonRequirement(specifier, self._python_candidate)
595 |
596 | def get_wheel_cache_entry(
597 | self, link: Link, name: Optional[str]
598 | ) -> Optional[CacheEntry]:
599 | """Look up the link in the wheel cache.
600 |
601 | If ``preparer.require_hashes`` is True, don't use the wheel cache,
602 | because cached wheels, always built locally, have different hashes
603 | than the files downloaded from the index server and thus throw false
604 | hash mismatches. Furthermore, cached wheels at present have
605 | nondeterministic contents due to file modification times.
606 | """
607 | if self._wheel_cache is None:
608 | return None
609 | return self._wheel_cache.get_cache_entry(
610 | link=link,
611 | package_name=name,
612 | supported_tags=self._supported_tags_cache,
613 | )
614 |
615 | def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]:
616 | # TODO: Are there more cases this needs to return True? Editable?
617 | dist = self._installed_dists.get(candidate.project_name)
618 | if dist is None: # Not installed, no uninstallation required.
619 | return None
620 |
621 | # We're installing into global site. The current installation must
622 | # be uninstalled, no matter it's in global or user site, because the
623 | # user site installation has precedence over global.
624 | if not self._use_user_site:
625 | return dist
626 |
627 | # We're installing into user site. Remove the user site installation.
628 | if dist.in_usersite:
629 | return dist
630 |
631 | # We're installing into user site, but the installed incompatible
632 | # package is in global site. We can't uninstall that, and would let
633 | # the new user installation to "shadow" it. But shadowing won't work
634 | # in virtual environments, so we error out.
635 | if running_under_virtualenv() and dist.in_site_packages:
636 | message = (
637 | f"Will not install to the user site because it will lack "
638 | f"sys.path precedence to {dist.raw_name} in {dist.location}"
639 | )
640 | raise InstallationError(message)
641 | return None
642 |
643 | def _report_requires_python_error(
644 | self, causes: Sequence["ConflictCause"]
645 | ) -> UnsupportedPythonVersion:
646 | assert causes, "Requires-Python error reported with no cause"
647 |
648 | version = self._python_candidate.version
649 |
650 | if len(causes) == 1:
651 | specifier = str(causes[0].requirement.specifier)
652 | message = (
653 | f"Package {causes[0].parent.name!r} requires a different "
654 | f"Python: {version} not in {specifier!r}"
655 | )
656 | return UnsupportedPythonVersion(message)
657 |
658 | message = f"Packages require a different Python. {version} not in:"
659 | for cause in causes:
660 | package = cause.parent.format_for_error()
661 | specifier = str(cause.requirement.specifier)
662 | message += f"\n{specifier!r} (required by {package})"
663 | return UnsupportedPythonVersion(message)
664 |
665 | def _report_single_requirement_conflict(
666 | self, req: Requirement, parent: Optional[Candidate]
667 | ) -> DistributionNotFound:
668 | if parent is None:
669 | req_disp = str(req)
670 | else:
671 | req_disp = f"{req} (from {parent.name})"
672 |
673 | cands = self._finder.find_all_candidates(req.project_name)
674 | skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
675 |
676 | versions_set: Set[Version] = set()
677 | yanked_versions_set: Set[Version] = set()
678 | for c in cands:
679 | is_yanked = c.link.is_yanked if c.link else False
680 | if is_yanked:
681 | yanked_versions_set.add(c.version)
682 | else:
683 | versions_set.add(c.version)
684 |
685 | versions = [str(v) for v in sorted(versions_set)]
686 | yanked_versions = [str(v) for v in sorted(yanked_versions_set)]
687 |
688 | if yanked_versions:
689 | # Saying "version X is yanked" isn't entirely accurate.
690 | # https://github.com/pypa/pip/issues/11745#issuecomment-1402805842
691 | logger.critical(
692 | "Ignored the following yanked versions: %s",
693 | ", ".join(yanked_versions) or "none",
694 | )
695 | if skipped_by_requires_python:
696 | logger.critical(
697 | "Ignored the following versions that require a different python "
698 | "version: %s",
699 | "; ".join(skipped_by_requires_python) or "none",
700 | )
701 | logger.critical(
702 | "Could not find a version that satisfies the requirement %s "
703 | "(from versions: %s)",
704 | req_disp,
705 | ", ".join(versions) or "none",
706 | )
707 | if str(req) == "requirements.txt":
708 | logger.info(
709 | "HINT: You are attempting to install a package literally "
710 | 'named "requirements.txt" (which cannot exist). Consider '
711 | "using the '-r' flag to install the packages listed in "
712 | "requirements.txt"
713 | )
714 |
715 | return DistributionNotFound(f"No matching distribution found for {req}")
716 |
717 | def get_installation_error(
718 | self,
719 | e: "ResolutionImpossible[Requirement, Candidate]",
720 | constraints: Dict[str, Constraint],
721 | ) -> InstallationError:
722 | assert e.causes, "Installation error reported with no cause"
723 |
724 | # If one of the things we can't solve is "we need Python X.Y",
725 | # that is what we report.
726 | requires_python_causes = [
727 | cause
728 | for cause in e.causes
729 | if isinstance(cause.requirement, RequiresPythonRequirement)
730 | and not cause.requirement.is_satisfied_by(self._python_candidate)
731 | ]
732 | if requires_python_causes:
733 | # The comprehension above makes sure all Requirement instances are
734 | # RequiresPythonRequirement, so let's cast for convenience.
735 | return self._report_requires_python_error(
736 | cast("Sequence[ConflictCause]", requires_python_causes),
737 | )
738 |
739 | # Otherwise, we have a set of causes which can't all be satisfied
740 | # at once.
741 |
742 | # The simplest case is when we have *one* cause that can't be
743 | # satisfied. We just report that case.
744 | if len(e.causes) == 1:
745 | req, parent = e.causes[0]
746 | if req.name not in constraints:
747 | return self._report_single_requirement_conflict(req, parent)
748 |
749 | # OK, we now have a list of requirements that can't all be
750 | # satisfied at once.
751 |
752 | # A couple of formatting helpers
753 | def text_join(parts: List[str]) -> str:
754 | if len(parts) == 1:
755 | return parts[0]
756 |
757 | return ", ".join(parts[:-1]) + " and " + parts[-1]
758 |
759 | def describe_trigger(parent: Candidate) -> str:
760 | ireq = parent.get_install_requirement()
761 | if not ireq or not ireq.comes_from:
762 | return f"{parent.name}=={parent.version}"
763 | if isinstance(ireq.comes_from, InstallRequirement):
764 | return str(ireq.comes_from.name)
765 | return str(ireq.comes_from)
766 |
767 | triggers = set()
768 | for req, parent in e.causes:
769 | if parent is None:
770 | # This is a root requirement, so we can report it directly
771 | trigger = req.format_for_error()
772 | else:
773 | trigger = describe_trigger(parent)
774 | triggers.add(trigger)
775 |
776 | if triggers:
777 | info = text_join(sorted(triggers))
778 | else:
779 | info = "the requested packages"
780 |
781 | msg = (
782 | f"Cannot install {info} because these package versions "
783 | "have conflicting dependencies."
784 | )
785 | logger.critical(msg)
786 | msg = "\nThe conflict is caused by:"
787 |
788 | relevant_constraints = set()
789 | for req, parent in e.causes:
790 | if req.name in constraints:
791 | relevant_constraints.add(req.name)
792 | msg = msg + "\n "
793 | if parent:
794 | msg = msg + f"{parent.name} {parent.version} depends on "
795 | else:
796 | msg = msg + "The user requested "
797 | msg = msg + req.format_for_error()
798 | for key in relevant_constraints:
799 | spec = constraints[key].specifier
800 | msg += f"\n The user requested (constraint) {key}{spec}"
801 |
802 | msg = (
803 | msg
804 | + "\n\n"
805 | + "To fix this you could try to:\n"
806 | + "1. loosen the range of package versions you've specified\n"
807 | + "2. remove package versions to allow pip to attempt to solve "
808 | + "the dependency conflict\n"
809 | )
810 |
811 | logger.info(msg)
812 |
813 | return DistributionNotFound(
814 | "ResolutionImpossible: for help visit "
815 | "https://pip.pypa.io/en/latest/topics/dependency-resolution/"
816 | "#dealing-with-dependency-conflicts"
817 | )
818 |
```