This is page 60 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/_internal/req/req_file.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Requirements file parsing
3 | """
4 |
5 | import logging
6 | import optparse
7 | import os
8 | import re
9 | import shlex
10 | import urllib.parse
11 | from optparse import Values
12 | from typing import (
13 | TYPE_CHECKING,
14 | Any,
15 | Callable,
16 | Dict,
17 | Generator,
18 | Iterable,
19 | List,
20 | NoReturn,
21 | Optional,
22 | Tuple,
23 | )
24 |
25 | from pip._internal.cli import cmdoptions
26 | from pip._internal.exceptions import InstallationError, RequirementsFileParseError
27 | from pip._internal.models.search_scope import SearchScope
28 | from pip._internal.utils.encoding import auto_decode
29 |
30 | if TYPE_CHECKING:
31 | from pip._internal.index.package_finder import PackageFinder
32 | from pip._internal.network.session import PipSession
33 |
34 | __all__ = ["parse_requirements"]
35 |
36 | ReqFileLines = Iterable[Tuple[int, str]]
37 |
38 | LineParser = Callable[[str], Tuple[str, Values]]
39 |
40 | SCHEME_RE = re.compile(r"^(http|https|file):", re.I)
41 | COMMENT_RE = re.compile(r"(^|\s+)#.*$")
42 |
43 | # Matches environment variable-style values in '${MY_VARIABLE_1}' with the
44 | # variable name consisting of only uppercase letters, digits or the '_'
45 | # (underscore). This follows the POSIX standard defined in IEEE Std 1003.1,
46 | # 2013 Edition.
47 | ENV_VAR_RE = re.compile(r"(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})")
48 |
49 | SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [
50 | cmdoptions.index_url,
51 | cmdoptions.extra_index_url,
52 | cmdoptions.no_index,
53 | cmdoptions.constraints,
54 | cmdoptions.requirements,
55 | cmdoptions.editable,
56 | cmdoptions.find_links,
57 | cmdoptions.no_binary,
58 | cmdoptions.only_binary,
59 | cmdoptions.prefer_binary,
60 | cmdoptions.require_hashes,
61 | cmdoptions.pre,
62 | cmdoptions.trusted_host,
63 | cmdoptions.use_new_feature,
64 | ]
65 |
66 | # options to be passed to requirements
67 | SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [
68 | cmdoptions.global_options,
69 | cmdoptions.hash,
70 | cmdoptions.config_settings,
71 | ]
72 |
73 | SUPPORTED_OPTIONS_EDITABLE_REQ: List[Callable[..., optparse.Option]] = [
74 | cmdoptions.config_settings,
75 | ]
76 |
77 |
78 | # the 'dest' string values
79 | SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ]
80 | SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [
81 | str(o().dest) for o in SUPPORTED_OPTIONS_EDITABLE_REQ
82 | ]
83 |
84 | logger = logging.getLogger(__name__)
85 |
86 |
87 | class ParsedRequirement:
88 | def __init__(
89 | self,
90 | requirement: str,
91 | is_editable: bool,
92 | comes_from: str,
93 | constraint: bool,
94 | options: Optional[Dict[str, Any]] = None,
95 | line_source: Optional[str] = None,
96 | ) -> None:
97 | self.requirement = requirement
98 | self.is_editable = is_editable
99 | self.comes_from = comes_from
100 | self.options = options
101 | self.constraint = constraint
102 | self.line_source = line_source
103 |
104 |
105 | class ParsedLine:
106 | def __init__(
107 | self,
108 | filename: str,
109 | lineno: int,
110 | args: str,
111 | opts: Values,
112 | constraint: bool,
113 | ) -> None:
114 | self.filename = filename
115 | self.lineno = lineno
116 | self.opts = opts
117 | self.constraint = constraint
118 |
119 | if args:
120 | self.is_requirement = True
121 | self.is_editable = False
122 | self.requirement = args
123 | elif opts.editables:
124 | self.is_requirement = True
125 | self.is_editable = True
126 | # We don't support multiple -e on one line
127 | self.requirement = opts.editables[0]
128 | else:
129 | self.is_requirement = False
130 |
131 |
132 | def parse_requirements(
133 | filename: str,
134 | session: "PipSession",
135 | finder: Optional["PackageFinder"] = None,
136 | options: Optional[optparse.Values] = None,
137 | constraint: bool = False,
138 | ) -> Generator[ParsedRequirement, None, None]:
139 | """Parse a requirements file and yield ParsedRequirement instances.
140 |
141 | :param filename: Path or url of requirements file.
142 | :param session: PipSession instance.
143 | :param finder: Instance of pip.index.PackageFinder.
144 | :param options: cli options.
145 | :param constraint: If true, parsing a constraint file rather than
146 | requirements file.
147 | """
148 | line_parser = get_line_parser(finder)
149 | parser = RequirementsFileParser(session, line_parser)
150 |
151 | for parsed_line in parser.parse(filename, constraint):
152 | parsed_req = handle_line(
153 | parsed_line, options=options, finder=finder, session=session
154 | )
155 | if parsed_req is not None:
156 | yield parsed_req
157 |
158 |
159 | def preprocess(content: str) -> ReqFileLines:
160 | """Split, filter, and join lines, and return a line iterator
161 |
162 | :param content: the content of the requirements file
163 | """
164 | lines_enum: ReqFileLines = enumerate(content.splitlines(), start=1)
165 | lines_enum = join_lines(lines_enum)
166 | lines_enum = ignore_comments(lines_enum)
167 | lines_enum = expand_env_variables(lines_enum)
168 | return lines_enum
169 |
170 |
171 | def handle_requirement_line(
172 | line: ParsedLine,
173 | options: Optional[optparse.Values] = None,
174 | ) -> ParsedRequirement:
175 | # preserve for the nested code path
176 | line_comes_from = "{} {} (line {})".format(
177 | "-c" if line.constraint else "-r",
178 | line.filename,
179 | line.lineno,
180 | )
181 |
182 | assert line.is_requirement
183 |
184 | # get the options that apply to requirements
185 | if line.is_editable:
186 | supported_dest = SUPPORTED_OPTIONS_EDITABLE_REQ_DEST
187 | else:
188 | supported_dest = SUPPORTED_OPTIONS_REQ_DEST
189 | req_options = {}
190 | for dest in supported_dest:
191 | if dest in line.opts.__dict__ and line.opts.__dict__[dest]:
192 | req_options[dest] = line.opts.__dict__[dest]
193 |
194 | line_source = f"line {line.lineno} of {line.filename}"
195 | return ParsedRequirement(
196 | requirement=line.requirement,
197 | is_editable=line.is_editable,
198 | comes_from=line_comes_from,
199 | constraint=line.constraint,
200 | options=req_options,
201 | line_source=line_source,
202 | )
203 |
204 |
205 | def handle_option_line(
206 | opts: Values,
207 | filename: str,
208 | lineno: int,
209 | finder: Optional["PackageFinder"] = None,
210 | options: Optional[optparse.Values] = None,
211 | session: Optional["PipSession"] = None,
212 | ) -> None:
213 | if opts.hashes:
214 | logger.warning(
215 | "%s line %s has --hash but no requirement, and will be ignored.",
216 | filename,
217 | lineno,
218 | )
219 |
220 | if options:
221 | # percolate options upward
222 | if opts.require_hashes:
223 | options.require_hashes = opts.require_hashes
224 | if opts.features_enabled:
225 | options.features_enabled.extend(
226 | f for f in opts.features_enabled if f not in options.features_enabled
227 | )
228 |
229 | # set finder options
230 | if finder:
231 | find_links = finder.find_links
232 | index_urls = finder.index_urls
233 | no_index = finder.search_scope.no_index
234 | if opts.no_index is True:
235 | no_index = True
236 | index_urls = []
237 | if opts.index_url and not no_index:
238 | index_urls = [opts.index_url]
239 | if opts.extra_index_urls and not no_index:
240 | index_urls.extend(opts.extra_index_urls)
241 | if opts.find_links:
242 | # FIXME: it would be nice to keep track of the source
243 | # of the find_links: support a find-links local path
244 | # relative to a requirements file.
245 | value = opts.find_links[0]
246 | req_dir = os.path.dirname(os.path.abspath(filename))
247 | relative_to_reqs_file = os.path.join(req_dir, value)
248 | if os.path.exists(relative_to_reqs_file):
249 | value = relative_to_reqs_file
250 | find_links.append(value)
251 |
252 | if session:
253 | # We need to update the auth urls in session
254 | session.update_index_urls(index_urls)
255 |
256 | search_scope = SearchScope(
257 | find_links=find_links,
258 | index_urls=index_urls,
259 | no_index=no_index,
260 | )
261 | finder.search_scope = search_scope
262 |
263 | if opts.pre:
264 | finder.set_allow_all_prereleases()
265 |
266 | if opts.prefer_binary:
267 | finder.set_prefer_binary()
268 |
269 | if session:
270 | for host in opts.trusted_hosts or []:
271 | source = f"line {lineno} of {filename}"
272 | session.add_trusted_host(host, source=source)
273 |
274 |
275 | def handle_line(
276 | line: ParsedLine,
277 | options: Optional[optparse.Values] = None,
278 | finder: Optional["PackageFinder"] = None,
279 | session: Optional["PipSession"] = None,
280 | ) -> Optional[ParsedRequirement]:
281 | """Handle a single parsed requirements line; This can result in
282 | creating/yielding requirements, or updating the finder.
283 |
284 | :param line: The parsed line to be processed.
285 | :param options: CLI options.
286 | :param finder: The finder - updated by non-requirement lines.
287 | :param session: The session - updated by non-requirement lines.
288 |
289 | Returns a ParsedRequirement object if the line is a requirement line,
290 | otherwise returns None.
291 |
292 | For lines that contain requirements, the only options that have an effect
293 | are from SUPPORTED_OPTIONS_REQ, and they are scoped to the
294 | requirement. Other options from SUPPORTED_OPTIONS may be present, but are
295 | ignored.
296 |
297 | For lines that do not contain requirements, the only options that have an
298 | effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may
299 | be present, but are ignored. These lines may contain multiple options
300 | (although our docs imply only one is supported), and all our parsed and
301 | affect the finder.
302 | """
303 |
304 | if line.is_requirement:
305 | parsed_req = handle_requirement_line(line, options)
306 | return parsed_req
307 | else:
308 | handle_option_line(
309 | line.opts,
310 | line.filename,
311 | line.lineno,
312 | finder,
313 | options,
314 | session,
315 | )
316 | return None
317 |
318 |
319 | class RequirementsFileParser:
320 | def __init__(
321 | self,
322 | session: "PipSession",
323 | line_parser: LineParser,
324 | ) -> None:
325 | self._session = session
326 | self._line_parser = line_parser
327 |
328 | def parse(
329 | self, filename: str, constraint: bool
330 | ) -> Generator[ParsedLine, None, None]:
331 | """Parse a given file, yielding parsed lines."""
332 | yield from self._parse_and_recurse(filename, constraint)
333 |
334 | def _parse_and_recurse(
335 | self, filename: str, constraint: bool
336 | ) -> Generator[ParsedLine, None, None]:
337 | for line in self._parse_file(filename, constraint):
338 | if not line.is_requirement and (
339 | line.opts.requirements or line.opts.constraints
340 | ):
341 | # parse a nested requirements file
342 | if line.opts.requirements:
343 | req_path = line.opts.requirements[0]
344 | nested_constraint = False
345 | else:
346 | req_path = line.opts.constraints[0]
347 | nested_constraint = True
348 |
349 | # original file is over http
350 | if SCHEME_RE.search(filename):
351 | # do a url join so relative paths work
352 | req_path = urllib.parse.urljoin(filename, req_path)
353 | # original file and nested file are paths
354 | elif not SCHEME_RE.search(req_path):
355 | # do a join so relative paths work
356 | req_path = os.path.join(
357 | os.path.dirname(filename),
358 | req_path,
359 | )
360 |
361 | yield from self._parse_and_recurse(req_path, nested_constraint)
362 | else:
363 | yield line
364 |
365 | def _parse_file(
366 | self, filename: str, constraint: bool
367 | ) -> Generator[ParsedLine, None, None]:
368 | _, content = get_file_content(filename, self._session)
369 |
370 | lines_enum = preprocess(content)
371 |
372 | for line_number, line in lines_enum:
373 | try:
374 | args_str, opts = self._line_parser(line)
375 | except OptionParsingError as e:
376 | # add offending line
377 | msg = f"Invalid requirement: {line}\n{e.msg}"
378 | raise RequirementsFileParseError(msg)
379 |
380 | yield ParsedLine(
381 | filename,
382 | line_number,
383 | args_str,
384 | opts,
385 | constraint,
386 | )
387 |
388 |
389 | def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser:
390 | def parse_line(line: str) -> Tuple[str, Values]:
391 | # Build new parser for each line since it accumulates appendable
392 | # options.
393 | parser = build_parser()
394 | defaults = parser.get_default_values()
395 | defaults.index_url = None
396 | if finder:
397 | defaults.format_control = finder.format_control
398 |
399 | args_str, options_str = break_args_options(line)
400 |
401 | try:
402 | options = shlex.split(options_str)
403 | except ValueError as e:
404 | raise OptionParsingError(f"Could not split options: {options_str}") from e
405 |
406 | opts, _ = parser.parse_args(options, defaults)
407 |
408 | return args_str, opts
409 |
410 | return parse_line
411 |
412 |
413 | def break_args_options(line: str) -> Tuple[str, str]:
414 | """Break up the line into an args and options string. We only want to shlex
415 | (and then optparse) the options, not the args. args can contain markers
416 | which are corrupted by shlex.
417 | """
418 | tokens = line.split(" ")
419 | args = []
420 | options = tokens[:]
421 | for token in tokens:
422 | if token.startswith("-") or token.startswith("--"):
423 | break
424 | else:
425 | args.append(token)
426 | options.pop(0)
427 | return " ".join(args), " ".join(options)
428 |
429 |
430 | class OptionParsingError(Exception):
431 | def __init__(self, msg: str) -> None:
432 | self.msg = msg
433 |
434 |
435 | def build_parser() -> optparse.OptionParser:
436 | """
437 | Return a parser for parsing requirement lines
438 | """
439 | parser = optparse.OptionParser(add_help_option=False)
440 |
441 | option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ
442 | for option_factory in option_factories:
443 | option = option_factory()
444 | parser.add_option(option)
445 |
446 | # By default optparse sys.exits on parsing errors. We want to wrap
447 | # that in our own exception.
448 | def parser_exit(self: Any, msg: str) -> "NoReturn":
449 | raise OptionParsingError(msg)
450 |
451 | # NOTE: mypy disallows assigning to a method
452 | # https://github.com/python/mypy/issues/2427
453 | parser.exit = parser_exit # type: ignore
454 |
455 | return parser
456 |
457 |
458 | def join_lines(lines_enum: ReqFileLines) -> ReqFileLines:
459 | """Joins a line ending in '\' with the previous line (except when following
460 | comments). The joined line takes on the index of the first line.
461 | """
462 | primary_line_number = None
463 | new_line: List[str] = []
464 | for line_number, line in lines_enum:
465 | if not line.endswith("\\") or COMMENT_RE.match(line):
466 | if COMMENT_RE.match(line):
467 | # this ensures comments are always matched later
468 | line = " " + line
469 | if new_line:
470 | new_line.append(line)
471 | assert primary_line_number is not None
472 | yield primary_line_number, "".join(new_line)
473 | new_line = []
474 | else:
475 | yield line_number, line
476 | else:
477 | if not new_line:
478 | primary_line_number = line_number
479 | new_line.append(line.strip("\\"))
480 |
481 | # last line contains \
482 | if new_line:
483 | assert primary_line_number is not None
484 | yield primary_line_number, "".join(new_line)
485 |
486 | # TODO: handle space after '\'.
487 |
488 |
489 | def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines:
490 | """
491 | Strips comments and filter empty lines.
492 | """
493 | for line_number, line in lines_enum:
494 | line = COMMENT_RE.sub("", line)
495 | line = line.strip()
496 | if line:
497 | yield line_number, line
498 |
499 |
500 | def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines:
501 | """Replace all environment variables that can be retrieved via `os.getenv`.
502 |
503 | The only allowed format for environment variables defined in the
504 | requirement file is `${MY_VARIABLE_1}` to ensure two things:
505 |
506 | 1. Strings that contain a `$` aren't accidentally (partially) expanded.
507 | 2. Ensure consistency across platforms for requirement files.
508 |
509 | These points are the result of a discussion on the `github pull
510 | request #3514 <https://github.com/pypa/pip/pull/3514>`_.
511 |
512 | Valid characters in variable names follow the `POSIX standard
513 | <http://pubs.opengroup.org/onlinepubs/9699919799/>`_ and are limited
514 | to uppercase letter, digits and the `_` (underscore).
515 | """
516 | for line_number, line in lines_enum:
517 | for env_var, var_name in ENV_VAR_RE.findall(line):
518 | value = os.getenv(var_name)
519 | if not value:
520 | continue
521 |
522 | line = line.replace(env_var, value)
523 |
524 | yield line_number, line
525 |
526 |
527 | def get_file_content(url: str, session: "PipSession") -> Tuple[str, str]:
528 | """Gets the content of a file; it may be a filename, file: URL, or
529 | http: URL. Returns (location, content). Content is unicode.
530 | Respects # -*- coding: declarations on the retrieved files.
531 |
532 | :param url: File path or url.
533 | :param session: PipSession instance.
534 | """
535 | scheme = urllib.parse.urlsplit(url).scheme
536 | # Pip has special support for file:// URLs (LocalFSAdapter).
537 | if scheme in ["http", "https", "file"]:
538 | # Delay importing heavy network modules until absolutely necessary.
539 | from pip._internal.network.utils import raise_for_status
540 |
541 | resp = session.get(url)
542 | raise_for_status(resp)
543 | return resp.url, resp.text
544 |
545 | # Assume this is a bare path.
546 | try:
547 | with open(url, "rb") as f:
548 | content = auto_decode(f.read())
549 | except OSError as exc:
550 | raise InstallationError(f"Could not open requirements file: {exc}")
551 | return url, content
552 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | lxml-based doctest output comparison.
3 |
4 | Note: normally, you should just import the `lxml.usedoctest` and
5 | `lxml.html.usedoctest` modules from within a doctest, instead of this
6 | one::
7 |
8 | >>> import lxml.usedoctest # for XML output
9 |
10 | >>> import lxml.html.usedoctest # for HTML output
11 |
12 | To use this module directly, you must call ``lxmldoctest.install()``,
13 | which will cause doctest to use this in all subsequent calls.
14 |
15 | This changes the way output is checked and comparisons are made for
16 | XML or HTML-like content.
17 |
18 | XML or HTML content is noticed because the example starts with ``<``
19 | (it's HTML if it starts with ``<html``). You can also use the
20 | ``PARSE_HTML`` and ``PARSE_XML`` flags to force parsing.
21 |
22 | Some rough wildcard-like things are allowed. Whitespace is generally
23 | ignored (except in attributes). In text (attributes and text in the
24 | body) you can use ``...`` as a wildcard. In an example it also
25 | matches any trailing tags in the element, though it does not match
26 | leading tags. You may create a tag ``<any>`` or include an ``any``
27 | attribute in the tag. An ``any`` tag matches any tag, while the
28 | attribute matches any and all attributes.
29 |
30 | When a match fails, the reformatted example and gotten text is
31 | displayed (indented), and a rough diff-like output is given. Anything
32 | marked with ``+`` is in the output but wasn't supposed to be, and
33 | similarly ``-`` means its in the example but wasn't in the output.
34 |
35 | You can disable parsing on one line with ``# doctest:+NOPARSE_MARKUP``
36 | """
37 |
38 | from lxml import etree
39 | import sys
40 | import re
41 | import doctest
42 | try:
43 | from html import escape as html_escape
44 | except ImportError:
45 | from cgi import escape as html_escape
46 |
47 | __all__ = ['PARSE_HTML', 'PARSE_XML', 'NOPARSE_MARKUP', 'LXMLOutputChecker',
48 | 'LHTMLOutputChecker', 'install', 'temp_install']
49 |
50 | PARSE_HTML = doctest.register_optionflag('PARSE_HTML')
51 | PARSE_XML = doctest.register_optionflag('PARSE_XML')
52 | NOPARSE_MARKUP = doctest.register_optionflag('NOPARSE_MARKUP')
53 |
54 | OutputChecker = doctest.OutputChecker
55 |
56 | def strip(v):
57 | if v is None:
58 | return None
59 | else:
60 | return v.strip()
61 |
62 | def norm_whitespace(v):
63 | return _norm_whitespace_re.sub(' ', v)
64 |
65 | _html_parser = etree.HTMLParser(recover=False, remove_blank_text=True)
66 |
67 | def html_fromstring(html):
68 | return etree.fromstring(html, _html_parser)
69 |
70 | # We use this to distinguish repr()s from elements:
71 | _repr_re = re.compile(r'^<[^>]+ (at|object) ')
72 | _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
73 |
74 | class LXMLOutputChecker(OutputChecker):
75 |
76 | empty_tags = (
77 | 'param', 'img', 'area', 'br', 'basefont', 'input',
78 | 'base', 'meta', 'link', 'col')
79 |
80 | def get_default_parser(self):
81 | return etree.XML
82 |
83 | def check_output(self, want, got, optionflags):
84 | alt_self = getattr(self, '_temp_override_self', None)
85 | if alt_self is not None:
86 | super_method = self._temp_call_super_check_output
87 | self = alt_self
88 | else:
89 | super_method = OutputChecker.check_output
90 | parser = self.get_parser(want, got, optionflags)
91 | if not parser:
92 | return super_method(
93 | self, want, got, optionflags)
94 | try:
95 | want_doc = parser(want)
96 | except etree.XMLSyntaxError:
97 | return False
98 | try:
99 | got_doc = parser(got)
100 | except etree.XMLSyntaxError:
101 | return False
102 | return self.compare_docs(want_doc, got_doc)
103 |
104 | def get_parser(self, want, got, optionflags):
105 | parser = None
106 | if NOPARSE_MARKUP & optionflags:
107 | return None
108 | if PARSE_HTML & optionflags:
109 | parser = html_fromstring
110 | elif PARSE_XML & optionflags:
111 | parser = etree.XML
112 | elif (want.strip().lower().startswith('<html')
113 | and got.strip().startswith('<html')):
114 | parser = html_fromstring
115 | elif (self._looks_like_markup(want)
116 | and self._looks_like_markup(got)):
117 | parser = self.get_default_parser()
118 | return parser
119 |
120 | def _looks_like_markup(self, s):
121 | s = s.strip()
122 | return (s.startswith('<')
123 | and not _repr_re.search(s))
124 |
125 | def compare_docs(self, want, got):
126 | if not self.tag_compare(want.tag, got.tag):
127 | return False
128 | if not self.text_compare(want.text, got.text, True):
129 | return False
130 | if not self.text_compare(want.tail, got.tail, True):
131 | return False
132 | if 'any' not in want.attrib:
133 | want_keys = sorted(want.attrib.keys())
134 | got_keys = sorted(got.attrib.keys())
135 | if want_keys != got_keys:
136 | return False
137 | for key in want_keys:
138 | if not self.text_compare(want.attrib[key], got.attrib[key], False):
139 | return False
140 | if want.text != '...' or len(want):
141 | want_children = list(want)
142 | got_children = list(got)
143 | while want_children or got_children:
144 | if not want_children or not got_children:
145 | return False
146 | want_first = want_children.pop(0)
147 | got_first = got_children.pop(0)
148 | if not self.compare_docs(want_first, got_first):
149 | return False
150 | if not got_children and want_first.tail == '...':
151 | break
152 | return True
153 |
154 | def text_compare(self, want, got, strip):
155 | want = want or ''
156 | got = got or ''
157 | if strip:
158 | want = norm_whitespace(want).strip()
159 | got = norm_whitespace(got).strip()
160 | want = '^%s$' % re.escape(want)
161 | want = want.replace(r'\.\.\.', '.*')
162 | if re.search(want, got):
163 | return True
164 | else:
165 | return False
166 |
167 | def tag_compare(self, want, got):
168 | if want == 'any':
169 | return True
170 | if (not isinstance(want, (str, bytes))
171 | or not isinstance(got, (str, bytes))):
172 | return want == got
173 | want = want or ''
174 | got = got or ''
175 | if want.startswith('{...}'):
176 | # Ellipsis on the namespace
177 | return want.split('}')[-1] == got.split('}')[-1]
178 | else:
179 | return want == got
180 |
181 | def output_difference(self, example, got, optionflags):
182 | want = example.want
183 | parser = self.get_parser(want, got, optionflags)
184 | errors = []
185 | if parser is not None:
186 | try:
187 | want_doc = parser(want)
188 | except etree.XMLSyntaxError:
189 | e = sys.exc_info()[1]
190 | errors.append('In example: %s' % e)
191 | try:
192 | got_doc = parser(got)
193 | except etree.XMLSyntaxError:
194 | e = sys.exc_info()[1]
195 | errors.append('In actual output: %s' % e)
196 | if parser is None or errors:
197 | value = OutputChecker.output_difference(
198 | self, example, got, optionflags)
199 | if errors:
200 | errors.append(value)
201 | return '\n'.join(errors)
202 | else:
203 | return value
204 | html = parser is html_fromstring
205 | diff_parts = ['Expected:',
206 | self.format_doc(want_doc, html, 2),
207 | 'Got:',
208 | self.format_doc(got_doc, html, 2),
209 | 'Diff:',
210 | self.collect_diff(want_doc, got_doc, html, 2)]
211 | return '\n'.join(diff_parts)
212 |
213 | def html_empty_tag(self, el, html=True):
214 | if not html:
215 | return False
216 | if el.tag not in self.empty_tags:
217 | return False
218 | if el.text or len(el):
219 | # This shouldn't happen (contents in an empty tag)
220 | return False
221 | return True
222 |
223 | def format_doc(self, doc, html, indent, prefix=''):
224 | parts = []
225 | if not len(doc):
226 | # No children...
227 | parts.append(' '*indent)
228 | parts.append(prefix)
229 | parts.append(self.format_tag(doc))
230 | if not self.html_empty_tag(doc, html):
231 | if strip(doc.text):
232 | parts.append(self.format_text(doc.text))
233 | parts.append(self.format_end_tag(doc))
234 | if strip(doc.tail):
235 | parts.append(self.format_text(doc.tail))
236 | parts.append('\n')
237 | return ''.join(parts)
238 | parts.append(' '*indent)
239 | parts.append(prefix)
240 | parts.append(self.format_tag(doc))
241 | if not self.html_empty_tag(doc, html):
242 | parts.append('\n')
243 | if strip(doc.text):
244 | parts.append(' '*indent)
245 | parts.append(self.format_text(doc.text))
246 | parts.append('\n')
247 | for el in doc:
248 | parts.append(self.format_doc(el, html, indent+2))
249 | parts.append(' '*indent)
250 | parts.append(self.format_end_tag(doc))
251 | parts.append('\n')
252 | if strip(doc.tail):
253 | parts.append(' '*indent)
254 | parts.append(self.format_text(doc.tail))
255 | parts.append('\n')
256 | return ''.join(parts)
257 |
258 | def format_text(self, text, strip=True):
259 | if text is None:
260 | return ''
261 | if strip:
262 | text = text.strip()
263 | return html_escape(text, 1)
264 |
265 | def format_tag(self, el):
266 | attrs = []
267 | if isinstance(el, etree.CommentBase):
268 | # FIXME: probably PIs should be handled specially too?
269 | return '<!--'
270 | for name, value in sorted(el.attrib.items()):
271 | attrs.append('%s="%s"' % (name, self.format_text(value, False)))
272 | if not attrs:
273 | return '<%s>' % el.tag
274 | return '<%s %s>' % (el.tag, ' '.join(attrs))
275 |
276 | def format_end_tag(self, el):
277 | if isinstance(el, etree.CommentBase):
278 | # FIXME: probably PIs should be handled specially too?
279 | return '-->'
280 | return '</%s>' % el.tag
281 |
282 | def collect_diff(self, want, got, html, indent):
283 | parts = []
284 | if not len(want) and not len(got):
285 | parts.append(' '*indent)
286 | parts.append(self.collect_diff_tag(want, got))
287 | if not self.html_empty_tag(got, html):
288 | parts.append(self.collect_diff_text(want.text, got.text))
289 | parts.append(self.collect_diff_end_tag(want, got))
290 | parts.append(self.collect_diff_text(want.tail, got.tail))
291 | parts.append('\n')
292 | return ''.join(parts)
293 | parts.append(' '*indent)
294 | parts.append(self.collect_diff_tag(want, got))
295 | parts.append('\n')
296 | if strip(want.text) or strip(got.text):
297 | parts.append(' '*indent)
298 | parts.append(self.collect_diff_text(want.text, got.text))
299 | parts.append('\n')
300 | want_children = list(want)
301 | got_children = list(got)
302 | while want_children or got_children:
303 | if not want_children:
304 | parts.append(self.format_doc(got_children.pop(0), html, indent+2, '+'))
305 | continue
306 | if not got_children:
307 | parts.append(self.format_doc(want_children.pop(0), html, indent+2, '-'))
308 | continue
309 | parts.append(self.collect_diff(
310 | want_children.pop(0), got_children.pop(0), html, indent+2))
311 | parts.append(' '*indent)
312 | parts.append(self.collect_diff_end_tag(want, got))
313 | parts.append('\n')
314 | if strip(want.tail) or strip(got.tail):
315 | parts.append(' '*indent)
316 | parts.append(self.collect_diff_text(want.tail, got.tail))
317 | parts.append('\n')
318 | return ''.join(parts)
319 |
320 | def collect_diff_tag(self, want, got):
321 | if not self.tag_compare(want.tag, got.tag):
322 | tag = '%s (got: %s)' % (want.tag, got.tag)
323 | else:
324 | tag = got.tag
325 | attrs = []
326 | any = want.tag == 'any' or 'any' in want.attrib
327 | for name, value in sorted(got.attrib.items()):
328 | if name not in want.attrib and not any:
329 | attrs.append('+%s="%s"' % (name, self.format_text(value, False)))
330 | else:
331 | if name in want.attrib:
332 | text = self.collect_diff_text(want.attrib[name], value, False)
333 | else:
334 | text = self.format_text(value, False)
335 | attrs.append('%s="%s"' % (name, text))
336 | if not any:
337 | for name, value in sorted(want.attrib.items()):
338 | if name in got.attrib:
339 | continue
340 | attrs.append('-%s="%s"' % (name, self.format_text(value, False)))
341 | if attrs:
342 | tag = '<%s %s>' % (tag, ' '.join(attrs))
343 | else:
344 | tag = '<%s>' % tag
345 | return tag
346 |
347 | def collect_diff_end_tag(self, want, got):
348 | if want.tag != got.tag:
349 | tag = '%s (got: %s)' % (want.tag, got.tag)
350 | else:
351 | tag = got.tag
352 | return '</%s>' % tag
353 |
354 | def collect_diff_text(self, want, got, strip=True):
355 | if self.text_compare(want, got, strip):
356 | if not got:
357 | return ''
358 | return self.format_text(got, strip)
359 | text = '%s (got: %s)' % (want, got)
360 | return self.format_text(text, strip)
361 |
362 | class LHTMLOutputChecker(LXMLOutputChecker):
363 | def get_default_parser(self):
364 | return html_fromstring
365 |
366 | def install(html=False):
367 | """
368 | Install doctestcompare for all future doctests.
369 |
370 | If html is true, then by default the HTML parser will be used;
371 | otherwise the XML parser is used.
372 | """
373 | if html:
374 | doctest.OutputChecker = LHTMLOutputChecker
375 | else:
376 | doctest.OutputChecker = LXMLOutputChecker
377 |
378 | def temp_install(html=False, del_module=None):
379 | """
380 | Use this *inside* a doctest to enable this checker for this
381 | doctest only.
382 |
383 | If html is true, then by default the HTML parser will be used;
384 | otherwise the XML parser is used.
385 | """
386 | if html:
387 | Checker = LHTMLOutputChecker
388 | else:
389 | Checker = LXMLOutputChecker
390 | frame = _find_doctest_frame()
391 | dt_self = frame.f_locals['self']
392 | checker = Checker()
393 | old_checker = dt_self._checker
394 | dt_self._checker = checker
395 | # The unfortunate thing is that there is a local variable 'check'
396 | # in the function that runs the doctests, that is a bound method
397 | # into the output checker. We have to update that. We can't
398 | # modify the frame, so we have to modify the object in place. The
399 | # only way to do this is to actually change the func_code
400 | # attribute of the method. We change it, and then wait for
401 | # __record_outcome to be run, which signals the end of the __run
402 | # method, at which point we restore the previous check_output
403 | # implementation.
404 | check_func = frame.f_locals['check'].__func__
405 | checker_check_func = checker.check_output.__func__
406 | # Because we can't patch up func_globals, this is the only global
407 | # in check_output that we care about:
408 | doctest.etree = etree
409 | _RestoreChecker(dt_self, old_checker, checker,
410 | check_func, checker_check_func,
411 | del_module)
412 |
413 | class _RestoreChecker:
414 | def __init__(self, dt_self, old_checker, new_checker, check_func, clone_func,
415 | del_module):
416 | self.dt_self = dt_self
417 | self.checker = old_checker
418 | self.checker._temp_call_super_check_output = self.call_super
419 | self.checker._temp_override_self = new_checker
420 | self.check_func = check_func
421 | self.clone_func = clone_func
422 | self.del_module = del_module
423 | self.install_clone()
424 | self.install_dt_self()
425 | def install_clone(self):
426 | self.func_code = self.check_func.__code__
427 | self.func_globals = self.check_func.__globals__
428 | self.check_func.__code__ = self.clone_func.__code__
429 | def uninstall_clone(self):
430 | self.check_func.__code__ = self.func_code
431 | def install_dt_self(self):
432 | self.prev_func = self.dt_self._DocTestRunner__record_outcome
433 | self.dt_self._DocTestRunner__record_outcome = self
434 | def uninstall_dt_self(self):
435 | self.dt_self._DocTestRunner__record_outcome = self.prev_func
436 | def uninstall_module(self):
437 | if self.del_module:
438 | import sys
439 | del sys.modules[self.del_module]
440 | if '.' in self.del_module:
441 | package, module = self.del_module.rsplit('.', 1)
442 | package_mod = sys.modules[package]
443 | delattr(package_mod, module)
444 | def __call__(self, *args, **kw):
445 | self.uninstall_clone()
446 | self.uninstall_dt_self()
447 | del self.checker._temp_override_self
448 | del self.checker._temp_call_super_check_output
449 | result = self.prev_func(*args, **kw)
450 | self.uninstall_module()
451 | return result
452 | def call_super(self, *args, **kw):
453 | self.uninstall_clone()
454 | try:
455 | return self.check_func(*args, **kw)
456 | finally:
457 | self.install_clone()
458 |
459 | def _find_doctest_frame():
460 | import sys
461 | frame = sys._getframe(1)
462 | while frame:
463 | l = frame.f_locals
464 | if 'BOOM' in l:
465 | # Sign of doctest
466 | return frame
467 | frame = frame.f_back
468 | raise LookupError(
469 | "Could not find doctest (only use this function *inside* a doctest)")
470 |
471 | __test__ = {
472 | 'basic': '''
473 | >>> temp_install()
474 | >>> print """<xml a="1" b="2">stuff</xml>"""
475 | <xml b="2" a="1">...</xml>
476 | >>> print """<xml xmlns="http://example.com"><tag attr="bar" /></xml>"""
477 | <xml xmlns="...">
478 | <tag attr="..." />
479 | </xml>
480 | >>> print """<xml>blahblahblah<foo /></xml>""" # doctest: +NOPARSE_MARKUP, +ELLIPSIS
481 | <xml>...foo /></xml>
482 | '''}
483 |
484 | if __name__ == '__main__':
485 | import doctest
486 | doctest.testmod()
487 |
488 |
489 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py:
--------------------------------------------------------------------------------
```python
1 | import contextlib
2 | import ssl
3 | import typing
4 | from ctypes import WinDLL # type: ignore
5 | from ctypes import WinError # type: ignore
6 | from ctypes import (
7 | POINTER,
8 | Structure,
9 | c_char_p,
10 | c_ulong,
11 | c_void_p,
12 | c_wchar_p,
13 | cast,
14 | create_unicode_buffer,
15 | pointer,
16 | sizeof,
17 | )
18 | from ctypes.wintypes import (
19 | BOOL,
20 | DWORD,
21 | HANDLE,
22 | LONG,
23 | LPCSTR,
24 | LPCVOID,
25 | LPCWSTR,
26 | LPFILETIME,
27 | LPSTR,
28 | LPWSTR,
29 | )
30 | from typing import TYPE_CHECKING, Any
31 |
32 | from ._ssl_constants import _set_ssl_context_verify_mode
33 |
34 | HCERTCHAINENGINE = HANDLE
35 | HCERTSTORE = HANDLE
36 | HCRYPTPROV_LEGACY = HANDLE
37 |
38 |
39 | class CERT_CONTEXT(Structure):
40 | _fields_ = (
41 | ("dwCertEncodingType", DWORD),
42 | ("pbCertEncoded", c_void_p),
43 | ("cbCertEncoded", DWORD),
44 | ("pCertInfo", c_void_p),
45 | ("hCertStore", HCERTSTORE),
46 | )
47 |
48 |
49 | PCERT_CONTEXT = POINTER(CERT_CONTEXT)
50 | PCCERT_CONTEXT = POINTER(PCERT_CONTEXT)
51 |
52 |
53 | class CERT_ENHKEY_USAGE(Structure):
54 | _fields_ = (
55 | ("cUsageIdentifier", DWORD),
56 | ("rgpszUsageIdentifier", POINTER(LPSTR)),
57 | )
58 |
59 |
60 | PCERT_ENHKEY_USAGE = POINTER(CERT_ENHKEY_USAGE)
61 |
62 |
63 | class CERT_USAGE_MATCH(Structure):
64 | _fields_ = (
65 | ("dwType", DWORD),
66 | ("Usage", CERT_ENHKEY_USAGE),
67 | )
68 |
69 |
70 | class CERT_CHAIN_PARA(Structure):
71 | _fields_ = (
72 | ("cbSize", DWORD),
73 | ("RequestedUsage", CERT_USAGE_MATCH),
74 | ("RequestedIssuancePolicy", CERT_USAGE_MATCH),
75 | ("dwUrlRetrievalTimeout", DWORD),
76 | ("fCheckRevocationFreshnessTime", BOOL),
77 | ("dwRevocationFreshnessTime", DWORD),
78 | ("pftCacheResync", LPFILETIME),
79 | ("pStrongSignPara", c_void_p),
80 | ("dwStrongSignFlags", DWORD),
81 | )
82 |
83 |
84 | if TYPE_CHECKING:
85 | PCERT_CHAIN_PARA = pointer[CERT_CHAIN_PARA] # type: ignore[misc]
86 | else:
87 | PCERT_CHAIN_PARA = POINTER(CERT_CHAIN_PARA)
88 |
89 |
90 | class CERT_TRUST_STATUS(Structure):
91 | _fields_ = (
92 | ("dwErrorStatus", DWORD),
93 | ("dwInfoStatus", DWORD),
94 | )
95 |
96 |
97 | class CERT_CHAIN_ELEMENT(Structure):
98 | _fields_ = (
99 | ("cbSize", DWORD),
100 | ("pCertContext", PCERT_CONTEXT),
101 | ("TrustStatus", CERT_TRUST_STATUS),
102 | ("pRevocationInfo", c_void_p),
103 | ("pIssuanceUsage", PCERT_ENHKEY_USAGE),
104 | ("pApplicationUsage", PCERT_ENHKEY_USAGE),
105 | ("pwszExtendedErrorInfo", LPCWSTR),
106 | )
107 |
108 |
109 | PCERT_CHAIN_ELEMENT = POINTER(CERT_CHAIN_ELEMENT)
110 |
111 |
112 | class CERT_SIMPLE_CHAIN(Structure):
113 | _fields_ = (
114 | ("cbSize", DWORD),
115 | ("TrustStatus", CERT_TRUST_STATUS),
116 | ("cElement", DWORD),
117 | ("rgpElement", POINTER(PCERT_CHAIN_ELEMENT)),
118 | ("pTrustListInfo", c_void_p),
119 | ("fHasRevocationFreshnessTime", BOOL),
120 | ("dwRevocationFreshnessTime", DWORD),
121 | )
122 |
123 |
124 | PCERT_SIMPLE_CHAIN = POINTER(CERT_SIMPLE_CHAIN)
125 |
126 |
127 | class CERT_CHAIN_CONTEXT(Structure):
128 | _fields_ = (
129 | ("cbSize", DWORD),
130 | ("TrustStatus", CERT_TRUST_STATUS),
131 | ("cChain", DWORD),
132 | ("rgpChain", POINTER(PCERT_SIMPLE_CHAIN)),
133 | ("cLowerQualityChainContext", DWORD),
134 | ("rgpLowerQualityChainContext", c_void_p),
135 | ("fHasRevocationFreshnessTime", BOOL),
136 | ("dwRevocationFreshnessTime", DWORD),
137 | )
138 |
139 |
140 | PCERT_CHAIN_CONTEXT = POINTER(CERT_CHAIN_CONTEXT)
141 | PCCERT_CHAIN_CONTEXT = POINTER(PCERT_CHAIN_CONTEXT)
142 |
143 |
144 | class SSL_EXTRA_CERT_CHAIN_POLICY_PARA(Structure):
145 | _fields_ = (
146 | ("cbSize", DWORD),
147 | ("dwAuthType", DWORD),
148 | ("fdwChecks", DWORD),
149 | ("pwszServerName", LPCWSTR),
150 | )
151 |
152 |
153 | class CERT_CHAIN_POLICY_PARA(Structure):
154 | _fields_ = (
155 | ("cbSize", DWORD),
156 | ("dwFlags", DWORD),
157 | ("pvExtraPolicyPara", c_void_p),
158 | )
159 |
160 |
161 | PCERT_CHAIN_POLICY_PARA = POINTER(CERT_CHAIN_POLICY_PARA)
162 |
163 |
164 | class CERT_CHAIN_POLICY_STATUS(Structure):
165 | _fields_ = (
166 | ("cbSize", DWORD),
167 | ("dwError", DWORD),
168 | ("lChainIndex", LONG),
169 | ("lElementIndex", LONG),
170 | ("pvExtraPolicyStatus", c_void_p),
171 | )
172 |
173 |
174 | PCERT_CHAIN_POLICY_STATUS = POINTER(CERT_CHAIN_POLICY_STATUS)
175 |
176 |
177 | class CERT_CHAIN_ENGINE_CONFIG(Structure):
178 | _fields_ = (
179 | ("cbSize", DWORD),
180 | ("hRestrictedRoot", HCERTSTORE),
181 | ("hRestrictedTrust", HCERTSTORE),
182 | ("hRestrictedOther", HCERTSTORE),
183 | ("cAdditionalStore", DWORD),
184 | ("rghAdditionalStore", c_void_p),
185 | ("dwFlags", DWORD),
186 | ("dwUrlRetrievalTimeout", DWORD),
187 | ("MaximumCachedCertificates", DWORD),
188 | ("CycleDetectionModulus", DWORD),
189 | ("hExclusiveRoot", HCERTSTORE),
190 | ("hExclusiveTrustedPeople", HCERTSTORE),
191 | ("dwExclusiveFlags", DWORD),
192 | )
193 |
194 |
195 | PCERT_CHAIN_ENGINE_CONFIG = POINTER(CERT_CHAIN_ENGINE_CONFIG)
196 | PHCERTCHAINENGINE = POINTER(HCERTCHAINENGINE)
197 |
198 | X509_ASN_ENCODING = 0x00000001
199 | PKCS_7_ASN_ENCODING = 0x00010000
200 | CERT_STORE_PROV_MEMORY = b"Memory"
201 | CERT_STORE_ADD_USE_EXISTING = 2
202 | USAGE_MATCH_TYPE_OR = 1
203 | OID_PKIX_KP_SERVER_AUTH = c_char_p(b"1.3.6.1.5.5.7.3.1")
204 | CERT_CHAIN_REVOCATION_CHECK_END_CERT = 0x10000000
205 | CERT_CHAIN_REVOCATION_CHECK_CHAIN = 0x20000000
206 | CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS = 0x00000007
207 | CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG = 0x00000008
208 | CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG = 0x00000010
209 | CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG = 0x00000040
210 | CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG = 0x00000020
211 | CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG = 0x00000080
212 | CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS = 0x00000F00
213 | CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG = 0x00008000
214 | CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG = 0x00004000
215 | AUTHTYPE_SERVER = 2
216 | CERT_CHAIN_POLICY_SSL = 4
217 | FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
218 | FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
219 |
220 | # Flags to set for SSLContext.verify_mode=CERT_NONE
221 | CERT_CHAIN_POLICY_VERIFY_MODE_NONE_FLAGS = (
222 | CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS
223 | | CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG
224 | | CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG
225 | | CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG
226 | | CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG
227 | | CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG
228 | | CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS
229 | | CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG
230 | | CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG
231 | )
232 |
233 | wincrypt = WinDLL("crypt32.dll")
234 | kernel32 = WinDLL("kernel32.dll")
235 |
236 |
237 | def _handle_win_error(result: bool, _: Any, args: Any) -> Any:
238 | if not result:
239 | # Note, actually raises OSError after calling GetLastError and FormatMessage
240 | raise WinError()
241 | return args
242 |
243 |
244 | CertCreateCertificateChainEngine = wincrypt.CertCreateCertificateChainEngine
245 | CertCreateCertificateChainEngine.argtypes = (
246 | PCERT_CHAIN_ENGINE_CONFIG,
247 | PHCERTCHAINENGINE,
248 | )
249 | CertCreateCertificateChainEngine.errcheck = _handle_win_error
250 |
251 | CertOpenStore = wincrypt.CertOpenStore
252 | CertOpenStore.argtypes = (LPCSTR, DWORD, HCRYPTPROV_LEGACY, DWORD, c_void_p)
253 | CertOpenStore.restype = HCERTSTORE
254 | CertOpenStore.errcheck = _handle_win_error
255 |
256 | CertAddEncodedCertificateToStore = wincrypt.CertAddEncodedCertificateToStore
257 | CertAddEncodedCertificateToStore.argtypes = (
258 | HCERTSTORE,
259 | DWORD,
260 | c_char_p,
261 | DWORD,
262 | DWORD,
263 | PCCERT_CONTEXT,
264 | )
265 | CertAddEncodedCertificateToStore.restype = BOOL
266 |
267 | CertCreateCertificateContext = wincrypt.CertCreateCertificateContext
268 | CertCreateCertificateContext.argtypes = (DWORD, c_char_p, DWORD)
269 | CertCreateCertificateContext.restype = PCERT_CONTEXT
270 | CertCreateCertificateContext.errcheck = _handle_win_error
271 |
272 | CertGetCertificateChain = wincrypt.CertGetCertificateChain
273 | CertGetCertificateChain.argtypes = (
274 | HCERTCHAINENGINE,
275 | PCERT_CONTEXT,
276 | LPFILETIME,
277 | HCERTSTORE,
278 | PCERT_CHAIN_PARA,
279 | DWORD,
280 | c_void_p,
281 | PCCERT_CHAIN_CONTEXT,
282 | )
283 | CertGetCertificateChain.restype = BOOL
284 | CertGetCertificateChain.errcheck = _handle_win_error
285 |
286 | CertVerifyCertificateChainPolicy = wincrypt.CertVerifyCertificateChainPolicy
287 | CertVerifyCertificateChainPolicy.argtypes = (
288 | c_ulong,
289 | PCERT_CHAIN_CONTEXT,
290 | PCERT_CHAIN_POLICY_PARA,
291 | PCERT_CHAIN_POLICY_STATUS,
292 | )
293 | CertVerifyCertificateChainPolicy.restype = BOOL
294 |
295 | CertCloseStore = wincrypt.CertCloseStore
296 | CertCloseStore.argtypes = (HCERTSTORE, DWORD)
297 | CertCloseStore.restype = BOOL
298 | CertCloseStore.errcheck = _handle_win_error
299 |
300 | CertFreeCertificateChain = wincrypt.CertFreeCertificateChain
301 | CertFreeCertificateChain.argtypes = (PCERT_CHAIN_CONTEXT,)
302 |
303 | CertFreeCertificateContext = wincrypt.CertFreeCertificateContext
304 | CertFreeCertificateContext.argtypes = (PCERT_CONTEXT,)
305 |
306 | CertFreeCertificateChainEngine = wincrypt.CertFreeCertificateChainEngine
307 | CertFreeCertificateChainEngine.argtypes = (HCERTCHAINENGINE,)
308 |
309 | FormatMessageW = kernel32.FormatMessageW
310 | FormatMessageW.argtypes = (
311 | DWORD,
312 | LPCVOID,
313 | DWORD,
314 | DWORD,
315 | LPWSTR,
316 | DWORD,
317 | c_void_p,
318 | )
319 | FormatMessageW.restype = DWORD
320 |
321 |
322 | def _verify_peercerts_impl(
323 | ssl_context: ssl.SSLContext,
324 | cert_chain: list[bytes],
325 | server_hostname: str | None = None,
326 | ) -> None:
327 | """Verify the cert_chain from the server using Windows APIs."""
328 |
329 | # If the peer didn't send any certificates then
330 | # we can't do verification. Raise an error.
331 | if not cert_chain:
332 | raise ssl.SSLCertVerificationError("Peer sent no certificates to verify")
333 |
334 | pCertContext = None
335 | hIntermediateCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None)
336 | try:
337 | # Add intermediate certs to an in-memory cert store
338 | for cert_bytes in cert_chain[1:]:
339 | CertAddEncodedCertificateToStore(
340 | hIntermediateCertStore,
341 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
342 | cert_bytes,
343 | len(cert_bytes),
344 | CERT_STORE_ADD_USE_EXISTING,
345 | None,
346 | )
347 |
348 | # Cert context for leaf cert
349 | leaf_cert = cert_chain[0]
350 | pCertContext = CertCreateCertificateContext(
351 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, leaf_cert, len(leaf_cert)
352 | )
353 |
354 | # Chain params to match certs for serverAuth extended usage
355 | cert_enhkey_usage = CERT_ENHKEY_USAGE()
356 | cert_enhkey_usage.cUsageIdentifier = 1
357 | cert_enhkey_usage.rgpszUsageIdentifier = (c_char_p * 1)(OID_PKIX_KP_SERVER_AUTH)
358 | cert_usage_match = CERT_USAGE_MATCH()
359 | cert_usage_match.Usage = cert_enhkey_usage
360 | chain_params = CERT_CHAIN_PARA()
361 | chain_params.RequestedUsage = cert_usage_match
362 | chain_params.cbSize = sizeof(chain_params)
363 | pChainPara = pointer(chain_params)
364 |
365 | if ssl_context.verify_flags & ssl.VERIFY_CRL_CHECK_CHAIN:
366 | chain_flags = CERT_CHAIN_REVOCATION_CHECK_CHAIN
367 | elif ssl_context.verify_flags & ssl.VERIFY_CRL_CHECK_LEAF:
368 | chain_flags = CERT_CHAIN_REVOCATION_CHECK_END_CERT
369 | else:
370 | chain_flags = 0
371 |
372 | try:
373 | # First attempt to verify using the default Windows system trust roots
374 | # (default chain engine).
375 | _get_and_verify_cert_chain(
376 | ssl_context,
377 | None,
378 | hIntermediateCertStore,
379 | pCertContext,
380 | pChainPara,
381 | server_hostname,
382 | chain_flags=chain_flags,
383 | )
384 | except ssl.SSLCertVerificationError as e:
385 | # If that fails but custom CA certs have been added
386 | # to the SSLContext using load_verify_locations,
387 | # try verifying using a custom chain engine
388 | # that trusts the custom CA certs.
389 | custom_ca_certs: list[bytes] | None = ssl_context.get_ca_certs(
390 | binary_form=True
391 | )
392 | if custom_ca_certs:
393 | try:
394 | _verify_using_custom_ca_certs(
395 | ssl_context,
396 | custom_ca_certs,
397 | hIntermediateCertStore,
398 | pCertContext,
399 | pChainPara,
400 | server_hostname,
401 | chain_flags=chain_flags,
402 | )
403 | # Raise the original error, not the new error.
404 | except ssl.SSLCertVerificationError:
405 | raise e from None
406 | else:
407 | raise
408 | finally:
409 | CertCloseStore(hIntermediateCertStore, 0)
410 | if pCertContext:
411 | CertFreeCertificateContext(pCertContext)
412 |
413 |
414 | def _get_and_verify_cert_chain(
415 | ssl_context: ssl.SSLContext,
416 | hChainEngine: HCERTCHAINENGINE | None,
417 | hIntermediateCertStore: HCERTSTORE,
418 | pPeerCertContext: c_void_p,
419 | pChainPara: PCERT_CHAIN_PARA, # type: ignore[valid-type]
420 | server_hostname: str | None,
421 | chain_flags: int,
422 | ) -> None:
423 | ppChainContext = None
424 | try:
425 | # Get cert chain
426 | ppChainContext = pointer(PCERT_CHAIN_CONTEXT())
427 | CertGetCertificateChain(
428 | hChainEngine, # chain engine
429 | pPeerCertContext, # leaf cert context
430 | None, # current system time
431 | hIntermediateCertStore, # additional in-memory cert store
432 | pChainPara, # chain-building parameters
433 | chain_flags,
434 | None, # reserved
435 | ppChainContext, # the resulting chain context
436 | )
437 | pChainContext = ppChainContext.contents
438 |
439 | # Verify cert chain
440 | ssl_extra_cert_chain_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA()
441 | ssl_extra_cert_chain_policy_para.cbSize = sizeof(
442 | ssl_extra_cert_chain_policy_para
443 | )
444 | ssl_extra_cert_chain_policy_para.dwAuthType = AUTHTYPE_SERVER
445 | ssl_extra_cert_chain_policy_para.fdwChecks = 0
446 | if server_hostname:
447 | ssl_extra_cert_chain_policy_para.pwszServerName = c_wchar_p(server_hostname)
448 |
449 | chain_policy = CERT_CHAIN_POLICY_PARA()
450 | chain_policy.pvExtraPolicyPara = cast(
451 | pointer(ssl_extra_cert_chain_policy_para), c_void_p
452 | )
453 | if ssl_context.verify_mode == ssl.CERT_NONE:
454 | chain_policy.dwFlags |= CERT_CHAIN_POLICY_VERIFY_MODE_NONE_FLAGS
455 | if not ssl_context.check_hostname:
456 | chain_policy.dwFlags |= CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG
457 | chain_policy.cbSize = sizeof(chain_policy)
458 |
459 | pPolicyPara = pointer(chain_policy)
460 | policy_status = CERT_CHAIN_POLICY_STATUS()
461 | policy_status.cbSize = sizeof(policy_status)
462 | pPolicyStatus = pointer(policy_status)
463 | CertVerifyCertificateChainPolicy(
464 | CERT_CHAIN_POLICY_SSL,
465 | pChainContext,
466 | pPolicyPara,
467 | pPolicyStatus,
468 | )
469 |
470 | # Check status
471 | error_code = policy_status.dwError
472 | if error_code:
473 | # Try getting a human readable message for an error code.
474 | error_message_buf = create_unicode_buffer(1024)
475 | error_message_chars = FormatMessageW(
476 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
477 | None,
478 | error_code,
479 | 0,
480 | error_message_buf,
481 | sizeof(error_message_buf),
482 | None,
483 | )
484 |
485 | # See if we received a message for the error,
486 | # otherwise we use a generic error with the
487 | # error code and hope that it's search-able.
488 | if error_message_chars <= 0:
489 | error_message = f"Certificate chain policy error {error_code:#x} [{policy_status.lElementIndex}]"
490 | else:
491 | error_message = error_message_buf.value.strip()
492 |
493 | err = ssl.SSLCertVerificationError(error_message)
494 | err.verify_message = error_message
495 | err.verify_code = error_code
496 | raise err from None
497 | finally:
498 | if ppChainContext:
499 | CertFreeCertificateChain(ppChainContext.contents)
500 |
501 |
502 | def _verify_using_custom_ca_certs(
503 | ssl_context: ssl.SSLContext,
504 | custom_ca_certs: list[bytes],
505 | hIntermediateCertStore: HCERTSTORE,
506 | pPeerCertContext: c_void_p,
507 | pChainPara: PCERT_CHAIN_PARA, # type: ignore[valid-type]
508 | server_hostname: str | None,
509 | chain_flags: int,
510 | ) -> None:
511 | hChainEngine = None
512 | hRootCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None)
513 | try:
514 | # Add custom CA certs to an in-memory cert store
515 | for cert_bytes in custom_ca_certs:
516 | CertAddEncodedCertificateToStore(
517 | hRootCertStore,
518 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
519 | cert_bytes,
520 | len(cert_bytes),
521 | CERT_STORE_ADD_USE_EXISTING,
522 | None,
523 | )
524 |
525 | # Create a custom cert chain engine which exclusively trusts
526 | # certs from our hRootCertStore
527 | cert_chain_engine_config = CERT_CHAIN_ENGINE_CONFIG()
528 | cert_chain_engine_config.cbSize = sizeof(cert_chain_engine_config)
529 | cert_chain_engine_config.hExclusiveRoot = hRootCertStore
530 | pConfig = pointer(cert_chain_engine_config)
531 | phChainEngine = pointer(HCERTCHAINENGINE())
532 | CertCreateCertificateChainEngine(
533 | pConfig,
534 | phChainEngine,
535 | )
536 | hChainEngine = phChainEngine.contents
537 |
538 | # Get and verify a cert chain using the custom chain engine
539 | _get_and_verify_cert_chain(
540 | ssl_context,
541 | hChainEngine,
542 | hIntermediateCertStore,
543 | pPeerCertContext,
544 | pChainPara,
545 | server_hostname,
546 | chain_flags,
547 | )
548 | finally:
549 | if hChainEngine:
550 | CertFreeCertificateChainEngine(hChainEngine)
551 | CertCloseStore(hRootCertStore, 0)
552 |
553 |
554 | @contextlib.contextmanager
555 | def _configure_context(ctx: ssl.SSLContext) -> typing.Iterator[None]:
556 | check_hostname = ctx.check_hostname
557 | verify_mode = ctx.verify_mode
558 | ctx.check_hostname = False
559 | _set_ssl_context_verify_mode(ctx, ssl.CERT_NONE)
560 | try:
561 | yield
562 | finally:
563 | ctx.check_hostname = check_hostname
564 | _set_ssl_context_verify_mode(ctx, verify_mode)
565 |
```