This is page 52 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/jinja2/sandbox.py:
--------------------------------------------------------------------------------
```python
1 | """A sandbox layer that ensures unsafe operations cannot be performed.
2 | Useful when the template itself comes from an untrusted source.
3 | """
4 |
5 | import operator
6 | import types
7 | import typing as t
8 | from collections import abc
9 | from collections import deque
10 | from string import Formatter
11 |
12 | from _string import formatter_field_name_split # type: ignore
13 | from markupsafe import EscapeFormatter
14 | from markupsafe import Markup
15 |
16 | from .environment import Environment
17 | from .exceptions import SecurityError
18 | from .runtime import Context
19 | from .runtime import Undefined
20 |
21 | F = t.TypeVar("F", bound=t.Callable[..., t.Any])
22 |
23 | #: maximum number of items a range may produce
24 | MAX_RANGE = 100000
25 |
26 | #: Unsafe function attributes.
27 | UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
28 |
29 | #: Unsafe method attributes. Function attributes are unsafe for methods too.
30 | UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
31 |
32 | #: unsafe generator attributes.
33 | UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
34 |
35 | #: unsafe attributes on coroutines
36 | UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
37 |
38 | #: unsafe attributes on async generators
39 | UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
40 |
41 | _mutable_spec: t.Tuple[t.Tuple[t.Type[t.Any], t.FrozenSet[str]], ...] = (
42 | (
43 | abc.MutableSet,
44 | frozenset(
45 | [
46 | "add",
47 | "clear",
48 | "difference_update",
49 | "discard",
50 | "pop",
51 | "remove",
52 | "symmetric_difference_update",
53 | "update",
54 | ]
55 | ),
56 | ),
57 | (
58 | abc.MutableMapping,
59 | frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
60 | ),
61 | (
62 | abc.MutableSequence,
63 | frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
64 | ),
65 | (
66 | deque,
67 | frozenset(
68 | [
69 | "append",
70 | "appendleft",
71 | "clear",
72 | "extend",
73 | "extendleft",
74 | "pop",
75 | "popleft",
76 | "remove",
77 | "rotate",
78 | ]
79 | ),
80 | ),
81 | )
82 |
83 |
84 | def inspect_format_method(callable: t.Callable[..., t.Any]) -> t.Optional[str]:
85 | if not isinstance(
86 | callable, (types.MethodType, types.BuiltinMethodType)
87 | ) or callable.__name__ not in ("format", "format_map"):
88 | return None
89 |
90 | obj = callable.__self__
91 |
92 | if isinstance(obj, str):
93 | return obj
94 |
95 | return None
96 |
97 |
98 | def safe_range(*args: int) -> range:
99 | """A range that can't generate ranges with a length of more than
100 | MAX_RANGE items.
101 | """
102 | rng = range(*args)
103 |
104 | if len(rng) > MAX_RANGE:
105 | raise OverflowError(
106 | "Range too big. The sandbox blocks ranges larger than"
107 | f" MAX_RANGE ({MAX_RANGE})."
108 | )
109 |
110 | return rng
111 |
112 |
113 | def unsafe(f: F) -> F:
114 | """Marks a function or method as unsafe.
115 |
116 | .. code-block: python
117 |
118 | @unsafe
119 | def delete(self):
120 | pass
121 | """
122 | f.unsafe_callable = True # type: ignore
123 | return f
124 |
125 |
126 | def is_internal_attribute(obj: t.Any, attr: str) -> bool:
127 | """Test if the attribute given is an internal python attribute. For
128 | example this function returns `True` for the `func_code` attribute of
129 | python objects. This is useful if the environment method
130 | :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
131 |
132 | >>> from jinja2.sandbox import is_internal_attribute
133 | >>> is_internal_attribute(str, "mro")
134 | True
135 | >>> is_internal_attribute(str, "upper")
136 | False
137 | """
138 | if isinstance(obj, types.FunctionType):
139 | if attr in UNSAFE_FUNCTION_ATTRIBUTES:
140 | return True
141 | elif isinstance(obj, types.MethodType):
142 | if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
143 | return True
144 | elif isinstance(obj, type):
145 | if attr == "mro":
146 | return True
147 | elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
148 | return True
149 | elif isinstance(obj, types.GeneratorType):
150 | if attr in UNSAFE_GENERATOR_ATTRIBUTES:
151 | return True
152 | elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
153 | if attr in UNSAFE_COROUTINE_ATTRIBUTES:
154 | return True
155 | elif hasattr(types, "AsyncGeneratorType") and isinstance(
156 | obj, types.AsyncGeneratorType
157 | ):
158 | if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
159 | return True
160 | return attr.startswith("__")
161 |
162 |
163 | def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
164 | """This function checks if an attribute on a builtin mutable object
165 | (list, dict, set or deque) or the corresponding ABCs would modify it
166 | if called.
167 |
168 | >>> modifies_known_mutable({}, "clear")
169 | True
170 | >>> modifies_known_mutable({}, "keys")
171 | False
172 | >>> modifies_known_mutable([], "append")
173 | True
174 | >>> modifies_known_mutable([], "index")
175 | False
176 |
177 | If called with an unsupported object, ``False`` is returned.
178 |
179 | >>> modifies_known_mutable("foo", "upper")
180 | False
181 | """
182 | for typespec, unsafe in _mutable_spec:
183 | if isinstance(obj, typespec):
184 | return attr in unsafe
185 | return False
186 |
187 |
188 | class SandboxedEnvironment(Environment):
189 | """The sandboxed environment. It works like the regular environment but
190 | tells the compiler to generate sandboxed code. Additionally subclasses of
191 | this environment may override the methods that tell the runtime what
192 | attributes or functions are safe to access.
193 |
194 | If the template tries to access insecure code a :exc:`SecurityError` is
195 | raised. However also other exceptions may occur during the rendering so
196 | the caller has to ensure that all exceptions are caught.
197 | """
198 |
199 | sandboxed = True
200 |
201 | #: default callback table for the binary operators. A copy of this is
202 | #: available on each instance of a sandboxed environment as
203 | #: :attr:`binop_table`
204 | default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
205 | "+": operator.add,
206 | "-": operator.sub,
207 | "*": operator.mul,
208 | "/": operator.truediv,
209 | "//": operator.floordiv,
210 | "**": operator.pow,
211 | "%": operator.mod,
212 | }
213 |
214 | #: default callback table for the unary operators. A copy of this is
215 | #: available on each instance of a sandboxed environment as
216 | #: :attr:`unop_table`
217 | default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
218 | "+": operator.pos,
219 | "-": operator.neg,
220 | }
221 |
222 | #: a set of binary operators that should be intercepted. Each operator
223 | #: that is added to this set (empty by default) is delegated to the
224 | #: :meth:`call_binop` method that will perform the operator. The default
225 | #: operator callback is specified by :attr:`binop_table`.
226 | #:
227 | #: The following binary operators are interceptable:
228 | #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
229 | #:
230 | #: The default operation form the operator table corresponds to the
231 | #: builtin function. Intercepted calls are always slower than the native
232 | #: operator call, so make sure only to intercept the ones you are
233 | #: interested in.
234 | #:
235 | #: .. versionadded:: 2.6
236 | intercepted_binops: t.FrozenSet[str] = frozenset()
237 |
238 | #: a set of unary operators that should be intercepted. Each operator
239 | #: that is added to this set (empty by default) is delegated to the
240 | #: :meth:`call_unop` method that will perform the operator. The default
241 | #: operator callback is specified by :attr:`unop_table`.
242 | #:
243 | #: The following unary operators are interceptable: ``+``, ``-``
244 | #:
245 | #: The default operation form the operator table corresponds to the
246 | #: builtin function. Intercepted calls are always slower than the native
247 | #: operator call, so make sure only to intercept the ones you are
248 | #: interested in.
249 | #:
250 | #: .. versionadded:: 2.6
251 | intercepted_unops: t.FrozenSet[str] = frozenset()
252 |
253 | def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
254 | super().__init__(*args, **kwargs)
255 | self.globals["range"] = safe_range
256 | self.binop_table = self.default_binop_table.copy()
257 | self.unop_table = self.default_unop_table.copy()
258 |
259 | def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
260 | """The sandboxed environment will call this method to check if the
261 | attribute of an object is safe to access. Per default all attributes
262 | starting with an underscore are considered private as well as the
263 | special attributes of internal python objects as returned by the
264 | :func:`is_internal_attribute` function.
265 | """
266 | return not (attr.startswith("_") or is_internal_attribute(obj, attr))
267 |
268 | def is_safe_callable(self, obj: t.Any) -> bool:
269 | """Check if an object is safely callable. By default callables
270 | are considered safe unless decorated with :func:`unsafe`.
271 |
272 | This also recognizes the Django convention of setting
273 | ``func.alters_data = True``.
274 | """
275 | return not (
276 | getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
277 | )
278 |
279 | def call_binop(
280 | self, context: Context, operator: str, left: t.Any, right: t.Any
281 | ) -> t.Any:
282 | """For intercepted binary operator calls (:meth:`intercepted_binops`)
283 | this function is executed instead of the builtin operator. This can
284 | be used to fine tune the behavior of certain operators.
285 |
286 | .. versionadded:: 2.6
287 | """
288 | return self.binop_table[operator](left, right)
289 |
290 | def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
291 | """For intercepted unary operator calls (:meth:`intercepted_unops`)
292 | this function is executed instead of the builtin operator. This can
293 | be used to fine tune the behavior of certain operators.
294 |
295 | .. versionadded:: 2.6
296 | """
297 | return self.unop_table[operator](arg)
298 |
299 | def getitem(
300 | self, obj: t.Any, argument: t.Union[str, t.Any]
301 | ) -> t.Union[t.Any, Undefined]:
302 | """Subscribe an object from sandboxed code."""
303 | try:
304 | return obj[argument]
305 | except (TypeError, LookupError):
306 | if isinstance(argument, str):
307 | try:
308 | attr = str(argument)
309 | except Exception:
310 | pass
311 | else:
312 | try:
313 | value = getattr(obj, attr)
314 | except AttributeError:
315 | pass
316 | else:
317 | if self.is_safe_attribute(obj, argument, value):
318 | return value
319 | return self.unsafe_undefined(obj, argument)
320 | return self.undefined(obj=obj, name=argument)
321 |
322 | def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
323 | """Subscribe an object from sandboxed code and prefer the
324 | attribute. The attribute passed *must* be a bytestring.
325 | """
326 | try:
327 | value = getattr(obj, attribute)
328 | except AttributeError:
329 | try:
330 | return obj[attribute]
331 | except (TypeError, LookupError):
332 | pass
333 | else:
334 | if self.is_safe_attribute(obj, attribute, value):
335 | return value
336 | return self.unsafe_undefined(obj, attribute)
337 | return self.undefined(obj=obj, name=attribute)
338 |
339 | def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
340 | """Return an undefined object for unsafe attributes."""
341 | return self.undefined(
342 | f"access to attribute {attribute!r} of"
343 | f" {type(obj).__name__!r} object is unsafe.",
344 | name=attribute,
345 | obj=obj,
346 | exc=SecurityError,
347 | )
348 |
349 | def format_string(
350 | self,
351 | s: str,
352 | args: t.Tuple[t.Any, ...],
353 | kwargs: t.Dict[str, t.Any],
354 | format_func: t.Optional[t.Callable[..., t.Any]] = None,
355 | ) -> str:
356 | """If a format call is detected, then this is routed through this
357 | method so that our safety sandbox can be used for it.
358 | """
359 | formatter: SandboxedFormatter
360 | if isinstance(s, Markup):
361 | formatter = SandboxedEscapeFormatter(self, escape=s.escape)
362 | else:
363 | formatter = SandboxedFormatter(self)
364 |
365 | if format_func is not None and format_func.__name__ == "format_map":
366 | if len(args) != 1 or kwargs:
367 | raise TypeError(
368 | "format_map() takes exactly one argument"
369 | f" {len(args) + (kwargs is not None)} given"
370 | )
371 |
372 | kwargs = args[0]
373 | args = ()
374 |
375 | rv = formatter.vformat(s, args, kwargs)
376 | return type(s)(rv)
377 |
378 | def call(
379 | __self, # noqa: B902
380 | __context: Context,
381 | __obj: t.Any,
382 | *args: t.Any,
383 | **kwargs: t.Any,
384 | ) -> t.Any:
385 | """Call an object from sandboxed code."""
386 | fmt = inspect_format_method(__obj)
387 | if fmt is not None:
388 | return __self.format_string(fmt, args, kwargs, __obj)
389 |
390 | # the double prefixes are to avoid double keyword argument
391 | # errors when proxying the call.
392 | if not __self.is_safe_callable(__obj):
393 | raise SecurityError(f"{__obj!r} is not safely callable")
394 | return __context.call(__obj, *args, **kwargs)
395 |
396 |
397 | class ImmutableSandboxedEnvironment(SandboxedEnvironment):
398 | """Works exactly like the regular `SandboxedEnvironment` but does not
399 | permit modifications on the builtin mutable objects `list`, `set`, and
400 | `dict` by using the :func:`modifies_known_mutable` function.
401 | """
402 |
403 | def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
404 | if not super().is_safe_attribute(obj, attr, value):
405 | return False
406 |
407 | return not modifies_known_mutable(obj, attr)
408 |
409 |
410 | class SandboxedFormatter(Formatter):
411 | def __init__(self, env: Environment, **kwargs: t.Any) -> None:
412 | self._env = env
413 | super().__init__(**kwargs)
414 |
415 | def get_field(
416 | self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
417 | ) -> t.Tuple[t.Any, str]:
418 | first, rest = formatter_field_name_split(field_name)
419 | obj = self.get_value(first, args, kwargs)
420 | for is_attr, i in rest:
421 | if is_attr:
422 | obj = self._env.getattr(obj, i)
423 | else:
424 | obj = self._env.getitem(obj, i)
425 | return obj, first
426 |
427 |
428 | class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
429 | pass
430 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/flask/sessions.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import hashlib
4 | import typing as t
5 | from collections.abc import MutableMapping
6 | from datetime import datetime
7 | from datetime import timezone
8 |
9 | from itsdangerous import BadSignature
10 | from itsdangerous import URLSafeTimedSerializer
11 | from werkzeug.datastructures import CallbackDict
12 |
13 | from .json.tag import TaggedJSONSerializer
14 |
15 | if t.TYPE_CHECKING: # pragma: no cover
16 | import typing_extensions as te
17 |
18 | from .app import Flask
19 | from .wrappers import Request
20 | from .wrappers import Response
21 |
22 |
23 | # TODO generic when Python > 3.8
24 | class SessionMixin(MutableMapping): # type: ignore[type-arg]
25 | """Expands a basic dictionary with session attributes."""
26 |
27 | @property
28 | def permanent(self) -> bool:
29 | """This reflects the ``'_permanent'`` key in the dict."""
30 | return self.get("_permanent", False)
31 |
32 | @permanent.setter
33 | def permanent(self, value: bool) -> None:
34 | self["_permanent"] = bool(value)
35 |
36 | #: Some implementations can detect whether a session is newly
37 | #: created, but that is not guaranteed. Use with caution. The mixin
38 | # default is hard-coded ``False``.
39 | new = False
40 |
41 | #: Some implementations can detect changes to the session and set
42 | #: this when that happens. The mixin default is hard coded to
43 | #: ``True``.
44 | modified = True
45 |
46 | #: Some implementations can detect when session data is read or
47 | #: written and set this when that happens. The mixin default is hard
48 | #: coded to ``True``.
49 | accessed = True
50 |
51 |
52 | # TODO generic when Python > 3.8
53 | class SecureCookieSession(CallbackDict, SessionMixin): # type: ignore[type-arg]
54 | """Base class for sessions based on signed cookies.
55 |
56 | This session backend will set the :attr:`modified` and
57 | :attr:`accessed` attributes. It cannot reliably track whether a
58 | session is new (vs. empty), so :attr:`new` remains hard coded to
59 | ``False``.
60 | """
61 |
62 | #: When data is changed, this is set to ``True``. Only the session
63 | #: dictionary itself is tracked; if the session contains mutable
64 | #: data (for example a nested dict) then this must be set to
65 | #: ``True`` manually when modifying that data. The session cookie
66 | #: will only be written to the response if this is ``True``.
67 | modified = False
68 |
69 | #: When data is read or written, this is set to ``True``. Used by
70 | # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
71 | #: header, which allows caching proxies to cache different pages for
72 | #: different users.
73 | accessed = False
74 |
75 | def __init__(self, initial: t.Any = None) -> None:
76 | def on_update(self: te.Self) -> None:
77 | self.modified = True
78 | self.accessed = True
79 |
80 | super().__init__(initial, on_update)
81 |
82 | def __getitem__(self, key: str) -> t.Any:
83 | self.accessed = True
84 | return super().__getitem__(key)
85 |
86 | def get(self, key: str, default: t.Any = None) -> t.Any:
87 | self.accessed = True
88 | return super().get(key, default)
89 |
90 | def setdefault(self, key: str, default: t.Any = None) -> t.Any:
91 | self.accessed = True
92 | return super().setdefault(key, default)
93 |
94 |
95 | class NullSession(SecureCookieSession):
96 | """Class used to generate nicer error messages if sessions are not
97 | available. Will still allow read-only access to the empty session
98 | but fail on setting.
99 | """
100 |
101 | def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
102 | raise RuntimeError(
103 | "The session is unavailable because no secret "
104 | "key was set. Set the secret_key on the "
105 | "application to something unique and secret."
106 | )
107 |
108 | __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950
109 | del _fail
110 |
111 |
112 | class SessionInterface:
113 | """The basic interface you have to implement in order to replace the
114 | default session interface which uses werkzeug's securecookie
115 | implementation. The only methods you have to implement are
116 | :meth:`open_session` and :meth:`save_session`, the others have
117 | useful defaults which you don't need to change.
118 |
119 | The session object returned by the :meth:`open_session` method has to
120 | provide a dictionary like interface plus the properties and methods
121 | from the :class:`SessionMixin`. We recommend just subclassing a dict
122 | and adding that mixin::
123 |
124 | class Session(dict, SessionMixin):
125 | pass
126 |
127 | If :meth:`open_session` returns ``None`` Flask will call into
128 | :meth:`make_null_session` to create a session that acts as replacement
129 | if the session support cannot work because some requirement is not
130 | fulfilled. The default :class:`NullSession` class that is created
131 | will complain that the secret key was not set.
132 |
133 | To replace the session interface on an application all you have to do
134 | is to assign :attr:`flask.Flask.session_interface`::
135 |
136 | app = Flask(__name__)
137 | app.session_interface = MySessionInterface()
138 |
139 | Multiple requests with the same session may be sent and handled
140 | concurrently. When implementing a new session interface, consider
141 | whether reads or writes to the backing store must be synchronized.
142 | There is no guarantee on the order in which the session for each
143 | request is opened or saved, it will occur in the order that requests
144 | begin and end processing.
145 |
146 | .. versionadded:: 0.8
147 | """
148 |
149 | #: :meth:`make_null_session` will look here for the class that should
150 | #: be created when a null session is requested. Likewise the
151 | #: :meth:`is_null_session` method will perform a typecheck against
152 | #: this type.
153 | null_session_class = NullSession
154 |
155 | #: A flag that indicates if the session interface is pickle based.
156 | #: This can be used by Flask extensions to make a decision in regards
157 | #: to how to deal with the session object.
158 | #:
159 | #: .. versionadded:: 0.10
160 | pickle_based = False
161 |
162 | def make_null_session(self, app: Flask) -> NullSession:
163 | """Creates a null session which acts as a replacement object if the
164 | real session support could not be loaded due to a configuration
165 | error. This mainly aids the user experience because the job of the
166 | null session is to still support lookup without complaining but
167 | modifications are answered with a helpful error message of what
168 | failed.
169 |
170 | This creates an instance of :attr:`null_session_class` by default.
171 | """
172 | return self.null_session_class()
173 |
174 | def is_null_session(self, obj: object) -> bool:
175 | """Checks if a given object is a null session. Null sessions are
176 | not asked to be saved.
177 |
178 | This checks if the object is an instance of :attr:`null_session_class`
179 | by default.
180 | """
181 | return isinstance(obj, self.null_session_class)
182 |
183 | def get_cookie_name(self, app: Flask) -> str:
184 | """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
185 | return app.config["SESSION_COOKIE_NAME"] # type: ignore[no-any-return]
186 |
187 | def get_cookie_domain(self, app: Flask) -> str | None:
188 | """The value of the ``Domain`` parameter on the session cookie. If not set,
189 | browsers will only send the cookie to the exact domain it was set from.
190 | Otherwise, they will send it to any subdomain of the given value as well.
191 |
192 | Uses the :data:`SESSION_COOKIE_DOMAIN` config.
193 |
194 | .. versionchanged:: 2.3
195 | Not set by default, does not fall back to ``SERVER_NAME``.
196 | """
197 | return app.config["SESSION_COOKIE_DOMAIN"] # type: ignore[no-any-return]
198 |
199 | def get_cookie_path(self, app: Flask) -> str:
200 | """Returns the path for which the cookie should be valid. The
201 | default implementation uses the value from the ``SESSION_COOKIE_PATH``
202 | config var if it's set, and falls back to ``APPLICATION_ROOT`` or
203 | uses ``/`` if it's ``None``.
204 | """
205 | return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] # type: ignore[no-any-return]
206 |
207 | def get_cookie_httponly(self, app: Flask) -> bool:
208 | """Returns True if the session cookie should be httponly. This
209 | currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
210 | config var.
211 | """
212 | return app.config["SESSION_COOKIE_HTTPONLY"] # type: ignore[no-any-return]
213 |
214 | def get_cookie_secure(self, app: Flask) -> bool:
215 | """Returns True if the cookie should be secure. This currently
216 | just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
217 | """
218 | return app.config["SESSION_COOKIE_SECURE"] # type: ignore[no-any-return]
219 |
220 | def get_cookie_samesite(self, app: Flask) -> str | None:
221 | """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
222 | ``SameSite`` attribute. This currently just returns the value of
223 | the :data:`SESSION_COOKIE_SAMESITE` setting.
224 | """
225 | return app.config["SESSION_COOKIE_SAMESITE"] # type: ignore[no-any-return]
226 |
227 | def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
228 | """A helper method that returns an expiration date for the session
229 | or ``None`` if the session is linked to the browser session. The
230 | default implementation returns now + the permanent session
231 | lifetime configured on the application.
232 | """
233 | if session.permanent:
234 | return datetime.now(timezone.utc) + app.permanent_session_lifetime
235 | return None
236 |
237 | def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
238 | """Used by session backends to determine if a ``Set-Cookie`` header
239 | should be set for this session cookie for this response. If the session
240 | has been modified, the cookie is set. If the session is permanent and
241 | the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is
242 | always set.
243 |
244 | This check is usually skipped if the session was deleted.
245 |
246 | .. versionadded:: 0.11
247 | """
248 |
249 | return session.modified or (
250 | session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
251 | )
252 |
253 | def open_session(self, app: Flask, request: Request) -> SessionMixin | None:
254 | """This is called at the beginning of each request, after
255 | pushing the request context, before matching the URL.
256 |
257 | This must return an object which implements a dictionary-like
258 | interface as well as the :class:`SessionMixin` interface.
259 |
260 | This will return ``None`` to indicate that loading failed in
261 | some way that is not immediately an error. The request
262 | context will fall back to using :meth:`make_null_session`
263 | in this case.
264 | """
265 | raise NotImplementedError()
266 |
267 | def save_session(
268 | self, app: Flask, session: SessionMixin, response: Response
269 | ) -> None:
270 | """This is called at the end of each request, after generating
271 | a response, before removing the request context. It is skipped
272 | if :meth:`is_null_session` returns ``True``.
273 | """
274 | raise NotImplementedError()
275 |
276 |
277 | session_json_serializer = TaggedJSONSerializer()
278 |
279 |
280 | def _lazy_sha1(string: bytes = b"") -> t.Any:
281 | """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include
282 | SHA-1, in which case the import and use as a default would fail before the
283 | developer can configure something else.
284 | """
285 | return hashlib.sha1(string)
286 |
287 |
288 | class SecureCookieSessionInterface(SessionInterface):
289 | """The default session interface that stores sessions in signed cookies
290 | through the :mod:`itsdangerous` module.
291 | """
292 |
293 | #: the salt that should be applied on top of the secret key for the
294 | #: signing of cookie based sessions.
295 | salt = "cookie-session"
296 | #: the hash function to use for the signature. The default is sha1
297 | digest_method = staticmethod(_lazy_sha1)
298 | #: the name of the itsdangerous supported key derivation. The default
299 | #: is hmac.
300 | key_derivation = "hmac"
301 | #: A python serializer for the payload. The default is a compact
302 | #: JSON derived serializer with support for some extra Python types
303 | #: such as datetime objects or tuples.
304 | serializer = session_json_serializer
305 | session_class = SecureCookieSession
306 |
307 | def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
308 | if not app.secret_key:
309 | return None
310 | signer_kwargs = dict(
311 | key_derivation=self.key_derivation, digest_method=self.digest_method
312 | )
313 | return URLSafeTimedSerializer(
314 | app.secret_key,
315 | salt=self.salt,
316 | serializer=self.serializer,
317 | signer_kwargs=signer_kwargs,
318 | )
319 |
320 | def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
321 | s = self.get_signing_serializer(app)
322 | if s is None:
323 | return None
324 | val = request.cookies.get(self.get_cookie_name(app))
325 | if not val:
326 | return self.session_class()
327 | max_age = int(app.permanent_session_lifetime.total_seconds())
328 | try:
329 | data = s.loads(val, max_age=max_age)
330 | return self.session_class(data)
331 | except BadSignature:
332 | return self.session_class()
333 |
334 | def save_session(
335 | self, app: Flask, session: SessionMixin, response: Response
336 | ) -> None:
337 | name = self.get_cookie_name(app)
338 | domain = self.get_cookie_domain(app)
339 | path = self.get_cookie_path(app)
340 | secure = self.get_cookie_secure(app)
341 | samesite = self.get_cookie_samesite(app)
342 | httponly = self.get_cookie_httponly(app)
343 |
344 | # Add a "Vary: Cookie" header if the session was accessed at all.
345 | if session.accessed:
346 | response.vary.add("Cookie")
347 |
348 | # If the session is modified to be empty, remove the cookie.
349 | # If the session is empty, return without setting the cookie.
350 | if not session:
351 | if session.modified:
352 | response.delete_cookie(
353 | name,
354 | domain=domain,
355 | path=path,
356 | secure=secure,
357 | samesite=samesite,
358 | httponly=httponly,
359 | )
360 | response.vary.add("Cookie")
361 |
362 | return
363 |
364 | if not self.should_set_cookie(app, session):
365 | return
366 |
367 | expires = self.get_expiration_time(app, session)
368 | val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore
369 | response.set_cookie(
370 | name,
371 | val, # type: ignore
372 | expires=expires,
373 | httponly=httponly,
374 | domain=domain,
375 | path=path,
376 | secure=secure,
377 | samesite=samesite,
378 | )
379 | response.vary.add("Cookie")
380 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/bs4/builder/_htmlparser.py:
--------------------------------------------------------------------------------
```python
1 | # encoding: utf-8
2 | """Use the HTMLParser library to parse HTML files that aren't too bad."""
3 |
4 | # Use of this source code is governed by the MIT license.
5 | __license__ = "MIT"
6 |
7 | __all__ = [
8 | 'HTMLParserTreeBuilder',
9 | ]
10 |
11 | from html.parser import HTMLParser
12 |
13 | import sys
14 | import warnings
15 |
16 | from bs4.element import (
17 | CData,
18 | Comment,
19 | Declaration,
20 | Doctype,
21 | ProcessingInstruction,
22 | )
23 | from bs4.dammit import EntitySubstitution, UnicodeDammit
24 |
25 | from bs4.builder import (
26 | DetectsXMLParsedAsHTML,
27 | ParserRejectedMarkup,
28 | HTML,
29 | HTMLTreeBuilder,
30 | STRICT,
31 | )
32 |
33 |
34 | HTMLPARSER = 'html.parser'
35 |
36 | class BeautifulSoupHTMLParser(HTMLParser, DetectsXMLParsedAsHTML):
37 | """A subclass of the Python standard library's HTMLParser class, which
38 | listens for HTMLParser events and translates them into calls
39 | to Beautiful Soup's tree construction API.
40 | """
41 |
42 | # Strategies for handling duplicate attributes
43 | IGNORE = 'ignore'
44 | REPLACE = 'replace'
45 |
46 | def __init__(self, *args, **kwargs):
47 | """Constructor.
48 |
49 | :param on_duplicate_attribute: A strategy for what to do if a
50 | tag includes the same attribute more than once. Accepted
51 | values are: REPLACE (replace earlier values with later
52 | ones, the default), IGNORE (keep the earliest value
53 | encountered), or a callable. A callable must take three
54 | arguments: the dictionary of attributes already processed,
55 | the name of the duplicate attribute, and the most recent value
56 | encountered.
57 | """
58 | self.on_duplicate_attribute = kwargs.pop(
59 | 'on_duplicate_attribute', self.REPLACE
60 | )
61 | HTMLParser.__init__(self, *args, **kwargs)
62 |
63 | # Keep a list of empty-element tags that were encountered
64 | # without an explicit closing tag. If we encounter a closing tag
65 | # of this type, we'll associate it with one of those entries.
66 | #
67 | # This isn't a stack because we don't care about the
68 | # order. It's a list of closing tags we've already handled and
69 | # will ignore, assuming they ever show up.
70 | self.already_closed_empty_element = []
71 |
72 | self._initialize_xml_detector()
73 |
74 | def error(self, message):
75 | # NOTE: This method is required so long as Python 3.9 is
76 | # supported. The corresponding code is removed from HTMLParser
77 | # in 3.5, but not removed from ParserBase until 3.10.
78 | # https://github.com/python/cpython/issues/76025
79 | #
80 | # The original implementation turned the error into a warning,
81 | # but in every case I discovered, this made HTMLParser
82 | # immediately crash with an error message that was less
83 | # helpful than the warning. The new implementation makes it
84 | # more clear that html.parser just can't parse this
85 | # markup. The 3.10 implementation does the same, though it
86 | # raises AssertionError rather than calling a method. (We
87 | # catch this error and wrap it in a ParserRejectedMarkup.)
88 | raise ParserRejectedMarkup(message)
89 |
90 | def handle_startendtag(self, name, attrs):
91 | """Handle an incoming empty-element tag.
92 |
93 | This is only called when the markup looks like <tag/>.
94 |
95 | :param name: Name of the tag.
96 | :param attrs: Dictionary of the tag's attributes.
97 | """
98 | # is_startend() tells handle_starttag not to close the tag
99 | # just because its name matches a known empty-element tag. We
100 | # know that this is an empty-element tag and we want to call
101 | # handle_endtag ourselves.
102 | tag = self.handle_starttag(name, attrs, handle_empty_element=False)
103 | self.handle_endtag(name)
104 |
105 | def handle_starttag(self, name, attrs, handle_empty_element=True):
106 | """Handle an opening tag, e.g. '<tag>'
107 |
108 | :param name: Name of the tag.
109 | :param attrs: Dictionary of the tag's attributes.
110 | :param handle_empty_element: True if this tag is known to be
111 | an empty-element tag (i.e. there is not expected to be any
112 | closing tag).
113 | """
114 | # XXX namespace
115 | attr_dict = {}
116 | for key, value in attrs:
117 | # Change None attribute values to the empty string
118 | # for consistency with the other tree builders.
119 | if value is None:
120 | value = ''
121 | if key in attr_dict:
122 | # A single attribute shows up multiple times in this
123 | # tag. How to handle it depends on the
124 | # on_duplicate_attribute setting.
125 | on_dupe = self.on_duplicate_attribute
126 | if on_dupe == self.IGNORE:
127 | pass
128 | elif on_dupe in (None, self.REPLACE):
129 | attr_dict[key] = value
130 | else:
131 | on_dupe(attr_dict, key, value)
132 | else:
133 | attr_dict[key] = value
134 | attrvalue = '""'
135 | #print("START", name)
136 | sourceline, sourcepos = self.getpos()
137 | tag = self.soup.handle_starttag(
138 | name, None, None, attr_dict, sourceline=sourceline,
139 | sourcepos=sourcepos
140 | )
141 | if tag and tag.is_empty_element and handle_empty_element:
142 | # Unlike other parsers, html.parser doesn't send separate end tag
143 | # events for empty-element tags. (It's handled in
144 | # handle_startendtag, but only if the original markup looked like
145 | # <tag/>.)
146 | #
147 | # So we need to call handle_endtag() ourselves. Since we
148 | # know the start event is identical to the end event, we
149 | # don't want handle_endtag() to cross off any previous end
150 | # events for tags of this name.
151 | self.handle_endtag(name, check_already_closed=False)
152 |
153 | # But we might encounter an explicit closing tag for this tag
154 | # later on. If so, we want to ignore it.
155 | self.already_closed_empty_element.append(name)
156 |
157 | if self._root_tag is None:
158 | self._root_tag_encountered(name)
159 |
160 | def handle_endtag(self, name, check_already_closed=True):
161 | """Handle a closing tag, e.g. '</tag>'
162 |
163 | :param name: A tag name.
164 | :param check_already_closed: True if this tag is expected to
165 | be the closing portion of an empty-element tag,
166 | e.g. '<tag></tag>'.
167 | """
168 | #print("END", name)
169 | if check_already_closed and name in self.already_closed_empty_element:
170 | # This is a redundant end tag for an empty-element tag.
171 | # We've already called handle_endtag() for it, so just
172 | # check it off the list.
173 | #print("ALREADY CLOSED", name)
174 | self.already_closed_empty_element.remove(name)
175 | else:
176 | self.soup.handle_endtag(name)
177 |
178 | def handle_data(self, data):
179 | """Handle some textual data that shows up between tags."""
180 | self.soup.handle_data(data)
181 |
182 | def handle_charref(self, name):
183 | """Handle a numeric character reference by converting it to the
184 | corresponding Unicode character and treating it as textual
185 | data.
186 |
187 | :param name: Character number, possibly in hexadecimal.
188 | """
189 | # TODO: This was originally a workaround for a bug in
190 | # HTMLParser. (http://bugs.python.org/issue13633) The bug has
191 | # been fixed, but removing this code still makes some
192 | # Beautiful Soup tests fail. This needs investigation.
193 | if name.startswith('x'):
194 | real_name = int(name.lstrip('x'), 16)
195 | elif name.startswith('X'):
196 | real_name = int(name.lstrip('X'), 16)
197 | else:
198 | real_name = int(name)
199 |
200 | data = None
201 | if real_name < 256:
202 | # HTML numeric entities are supposed to reference Unicode
203 | # code points, but sometimes they reference code points in
204 | # some other encoding (ahem, Windows-1252). E.g. “
205 | # instead of É for LEFT DOUBLE QUOTATION MARK. This
206 | # code tries to detect this situation and compensate.
207 | for encoding in (self.soup.original_encoding, 'windows-1252'):
208 | if not encoding:
209 | continue
210 | try:
211 | data = bytearray([real_name]).decode(encoding)
212 | except UnicodeDecodeError as e:
213 | pass
214 | if not data:
215 | try:
216 | data = chr(real_name)
217 | except (ValueError, OverflowError) as e:
218 | pass
219 | data = data or "\N{REPLACEMENT CHARACTER}"
220 | self.handle_data(data)
221 |
222 | def handle_entityref(self, name):
223 | """Handle a named entity reference by converting it to the
224 | corresponding Unicode character(s) and treating it as textual
225 | data.
226 |
227 | :param name: Name of the entity reference.
228 | """
229 | character = EntitySubstitution.HTML_ENTITY_TO_CHARACTER.get(name)
230 | if character is not None:
231 | data = character
232 | else:
233 | # If this were XML, it would be ambiguous whether "&foo"
234 | # was an character entity reference with a missing
235 | # semicolon or the literal string "&foo". Since this is
236 | # HTML, we have a complete list of all character entity references,
237 | # and this one wasn't found, so assume it's the literal string "&foo".
238 | data = "&%s" % name
239 | self.handle_data(data)
240 |
241 | def handle_comment(self, data):
242 | """Handle an HTML comment.
243 |
244 | :param data: The text of the comment.
245 | """
246 | self.soup.endData()
247 | self.soup.handle_data(data)
248 | self.soup.endData(Comment)
249 |
250 | def handle_decl(self, data):
251 | """Handle a DOCTYPE declaration.
252 |
253 | :param data: The text of the declaration.
254 | """
255 | self.soup.endData()
256 | data = data[len("DOCTYPE "):]
257 | self.soup.handle_data(data)
258 | self.soup.endData(Doctype)
259 |
260 | def unknown_decl(self, data):
261 | """Handle a declaration of unknown type -- probably a CDATA block.
262 |
263 | :param data: The text of the declaration.
264 | """
265 | if data.upper().startswith('CDATA['):
266 | cls = CData
267 | data = data[len('CDATA['):]
268 | else:
269 | cls = Declaration
270 | self.soup.endData()
271 | self.soup.handle_data(data)
272 | self.soup.endData(cls)
273 |
274 | def handle_pi(self, data):
275 | """Handle a processing instruction.
276 |
277 | :param data: The text of the instruction.
278 | """
279 | self.soup.endData()
280 | self.soup.handle_data(data)
281 | self._document_might_be_xml(data)
282 | self.soup.endData(ProcessingInstruction)
283 |
284 |
285 | class HTMLParserTreeBuilder(HTMLTreeBuilder):
286 | """A Beautiful soup `TreeBuilder` that uses the `HTMLParser` parser,
287 | found in the Python standard library.
288 | """
289 | is_xml = False
290 | picklable = True
291 | NAME = HTMLPARSER
292 | features = [NAME, HTML, STRICT]
293 |
294 | # The html.parser knows which line number and position in the
295 | # original file is the source of an element.
296 | TRACKS_LINE_NUMBERS = True
297 |
298 | def __init__(self, parser_args=None, parser_kwargs=None, **kwargs):
299 | """Constructor.
300 |
301 | :param parser_args: Positional arguments to pass into
302 | the BeautifulSoupHTMLParser constructor, once it's
303 | invoked.
304 | :param parser_kwargs: Keyword arguments to pass into
305 | the BeautifulSoupHTMLParser constructor, once it's
306 | invoked.
307 | :param kwargs: Keyword arguments for the superclass constructor.
308 | """
309 | # Some keyword arguments will be pulled out of kwargs and placed
310 | # into parser_kwargs.
311 | extra_parser_kwargs = dict()
312 | for arg in ('on_duplicate_attribute',):
313 | if arg in kwargs:
314 | value = kwargs.pop(arg)
315 | extra_parser_kwargs[arg] = value
316 | super(HTMLParserTreeBuilder, self).__init__(**kwargs)
317 | parser_args = parser_args or []
318 | parser_kwargs = parser_kwargs or {}
319 | parser_kwargs.update(extra_parser_kwargs)
320 | parser_kwargs['convert_charrefs'] = False
321 | self.parser_args = (parser_args, parser_kwargs)
322 |
323 | def prepare_markup(self, markup, user_specified_encoding=None,
324 | document_declared_encoding=None, exclude_encodings=None):
325 |
326 | """Run any preliminary steps necessary to make incoming markup
327 | acceptable to the parser.
328 |
329 | :param markup: Some markup -- probably a bytestring.
330 | :param user_specified_encoding: The user asked to try this encoding.
331 | :param document_declared_encoding: The markup itself claims to be
332 | in this encoding.
333 | :param exclude_encodings: The user asked _not_ to try any of
334 | these encodings.
335 |
336 | :yield: A series of 4-tuples:
337 | (markup, encoding, declared encoding,
338 | has undergone character replacement)
339 |
340 | Each 4-tuple represents a strategy for converting the
341 | document to Unicode and parsing it. Each strategy will be tried
342 | in turn.
343 | """
344 | if isinstance(markup, str):
345 | # Parse Unicode as-is.
346 | yield (markup, None, None, False)
347 | return
348 |
349 | # Ask UnicodeDammit to sniff the most likely encoding.
350 |
351 | # This was provided by the end-user; treat it as a known
352 | # definite encoding per the algorithm laid out in the HTML5
353 | # spec. (See the EncodingDetector class for details.)
354 | known_definite_encodings = [user_specified_encoding]
355 |
356 | # This was found in the document; treat it as a slightly lower-priority
357 | # user encoding.
358 | user_encodings = [document_declared_encoding]
359 |
360 | try_encodings = [user_specified_encoding, document_declared_encoding]
361 | dammit = UnicodeDammit(
362 | markup,
363 | known_definite_encodings=known_definite_encodings,
364 | user_encodings=user_encodings,
365 | is_html=True,
366 | exclude_encodings=exclude_encodings
367 | )
368 | yield (dammit.markup, dammit.original_encoding,
369 | dammit.declared_html_encoding,
370 | dammit.contains_replacement_characters)
371 |
372 | def feed(self, markup):
373 | """Run some incoming markup through some parsing process,
374 | populating the `BeautifulSoup` object in self.soup.
375 | """
376 | args, kwargs = self.parser_args
377 | parser = BeautifulSoupHTMLParser(*args, **kwargs)
378 | parser.soup = self.soup
379 | try:
380 | parser.feed(markup)
381 | parser.close()
382 | except AssertionError as e:
383 | # html.parser raises AssertionError in rare cases to
384 | # indicate a fatal problem with the markup, especially
385 | # when there's an error in the doctype declaration.
386 | raise ParserRejectedMarkup(e)
387 | parser.already_closed_empty_element = []
388 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py:
--------------------------------------------------------------------------------
```python
1 | import functools
2 | import logging
3 | import os
4 | import pathlib
5 | import sys
6 | import sysconfig
7 | from typing import Any, Dict, Generator, Optional, Tuple
8 |
9 | from pip._internal.models.scheme import SCHEME_KEYS, Scheme
10 | from pip._internal.utils.compat import WINDOWS
11 | from pip._internal.utils.deprecation import deprecated
12 | from pip._internal.utils.virtualenv import running_under_virtualenv
13 |
14 | from . import _sysconfig
15 | from .base import (
16 | USER_CACHE_DIR,
17 | get_major_minor_version,
18 | get_src_prefix,
19 | is_osx_framework,
20 | site_packages,
21 | user_site,
22 | )
23 |
24 | __all__ = [
25 | "USER_CACHE_DIR",
26 | "get_bin_prefix",
27 | "get_bin_user",
28 | "get_major_minor_version",
29 | "get_platlib",
30 | "get_purelib",
31 | "get_scheme",
32 | "get_src_prefix",
33 | "site_packages",
34 | "user_site",
35 | ]
36 |
37 |
38 | logger = logging.getLogger(__name__)
39 |
40 |
41 | _PLATLIBDIR: str = getattr(sys, "platlibdir", "lib")
42 |
43 | _USE_SYSCONFIG_DEFAULT = sys.version_info >= (3, 10)
44 |
45 |
46 | def _should_use_sysconfig() -> bool:
47 | """This function determines the value of _USE_SYSCONFIG.
48 |
49 | By default, pip uses sysconfig on Python 3.10+.
50 | But Python distributors can override this decision by setting:
51 | sysconfig._PIP_USE_SYSCONFIG = True / False
52 | Rationale in https://github.com/pypa/pip/issues/10647
53 |
54 | This is a function for testability, but should be constant during any one
55 | run.
56 | """
57 | return bool(getattr(sysconfig, "_PIP_USE_SYSCONFIG", _USE_SYSCONFIG_DEFAULT))
58 |
59 |
60 | _USE_SYSCONFIG = _should_use_sysconfig()
61 |
62 | if not _USE_SYSCONFIG:
63 | # Import distutils lazily to avoid deprecation warnings,
64 | # but import it soon enough that it is in memory and available during
65 | # a pip reinstall.
66 | from . import _distutils
67 |
68 | # Be noisy about incompatibilities if this platforms "should" be using
69 | # sysconfig, but is explicitly opting out and using distutils instead.
70 | if _USE_SYSCONFIG_DEFAULT and not _USE_SYSCONFIG:
71 | _MISMATCH_LEVEL = logging.WARNING
72 | else:
73 | _MISMATCH_LEVEL = logging.DEBUG
74 |
75 |
76 | def _looks_like_bpo_44860() -> bool:
77 | """The resolution to bpo-44860 will change this incorrect platlib.
78 |
79 | See <https://bugs.python.org/issue44860>.
80 | """
81 | from distutils.command.install import INSTALL_SCHEMES
82 |
83 | try:
84 | unix_user_platlib = INSTALL_SCHEMES["unix_user"]["platlib"]
85 | except KeyError:
86 | return False
87 | return unix_user_platlib == "$usersite"
88 |
89 |
90 | def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
91 | platlib = scheme["platlib"]
92 | if "/$platlibdir/" in platlib:
93 | platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/")
94 | if "/lib64/" not in platlib:
95 | return False
96 | unpatched = platlib.replace("/lib64/", "/lib/")
97 | return unpatched.replace("$platbase/", "$base/") == scheme["purelib"]
98 |
99 |
100 | @functools.lru_cache(maxsize=None)
101 | def _looks_like_red_hat_lib() -> bool:
102 | """Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
103 |
104 | This is the only way I can see to tell a Red Hat-patched Python.
105 | """
106 | from distutils.command.install import INSTALL_SCHEMES
107 |
108 | return all(
109 | k in INSTALL_SCHEMES
110 | and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k])
111 | for k in ("unix_prefix", "unix_home")
112 | )
113 |
114 |
115 | @functools.lru_cache(maxsize=None)
116 | def _looks_like_debian_scheme() -> bool:
117 | """Debian adds two additional schemes."""
118 | from distutils.command.install import INSTALL_SCHEMES
119 |
120 | return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
121 |
122 |
123 | @functools.lru_cache(maxsize=None)
124 | def _looks_like_red_hat_scheme() -> bool:
125 | """Red Hat patches ``sys.prefix`` and ``sys.exec_prefix``.
126 |
127 | Red Hat's ``00251-change-user-install-location.patch`` changes the install
128 | command's ``prefix`` and ``exec_prefix`` to append ``"/local"``. This is
129 | (fortunately?) done quite unconditionally, so we create a default command
130 | object without any configuration to detect this.
131 | """
132 | from distutils.command.install import install
133 | from distutils.dist import Distribution
134 |
135 | cmd: Any = install(Distribution())
136 | cmd.finalize_options()
137 | return (
138 | cmd.exec_prefix == f"{os.path.normpath(sys.exec_prefix)}/local"
139 | and cmd.prefix == f"{os.path.normpath(sys.prefix)}/local"
140 | )
141 |
142 |
143 | @functools.lru_cache(maxsize=None)
144 | def _looks_like_slackware_scheme() -> bool:
145 | """Slackware patches sysconfig but fails to patch distutils and site.
146 |
147 | Slackware changes sysconfig's user scheme to use ``"lib64"`` for the lib
148 | path, but does not do the same to the site module.
149 | """
150 | if user_site is None: # User-site not available.
151 | return False
152 | try:
153 | paths = sysconfig.get_paths(scheme="posix_user", expand=False)
154 | except KeyError: # User-site not available.
155 | return False
156 | return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site
157 |
158 |
159 | @functools.lru_cache(maxsize=None)
160 | def _looks_like_msys2_mingw_scheme() -> bool:
161 | """MSYS2 patches distutils and sysconfig to use a UNIX-like scheme.
162 |
163 | However, MSYS2 incorrectly patches sysconfig ``nt`` scheme. The fix is
164 | likely going to be included in their 3.10 release, so we ignore the warning.
165 | See msys2/MINGW-packages#9319.
166 |
167 | MSYS2 MINGW's patch uses lowercase ``"lib"`` instead of the usual uppercase,
168 | and is missing the final ``"site-packages"``.
169 | """
170 | paths = sysconfig.get_paths("nt", expand=False)
171 | return all(
172 | "Lib" not in p and "lib" in p and not p.endswith("site-packages")
173 | for p in (paths[key] for key in ("platlib", "purelib"))
174 | )
175 |
176 |
177 | def _fix_abiflags(parts: Tuple[str]) -> Generator[str, None, None]:
178 | ldversion = sysconfig.get_config_var("LDVERSION")
179 | abiflags = getattr(sys, "abiflags", None)
180 |
181 | # LDVERSION does not end with sys.abiflags. Just return the path unchanged.
182 | if not ldversion or not abiflags or not ldversion.endswith(abiflags):
183 | yield from parts
184 | return
185 |
186 | # Strip sys.abiflags from LDVERSION-based path components.
187 | for part in parts:
188 | if part.endswith(ldversion):
189 | part = part[: (0 - len(abiflags))]
190 | yield part
191 |
192 |
193 | @functools.lru_cache(maxsize=None)
194 | def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None:
195 | issue_url = "https://github.com/pypa/pip/issues/10151"
196 | message = (
197 | "Value for %s does not match. Please report this to <%s>"
198 | "\ndistutils: %s"
199 | "\nsysconfig: %s"
200 | )
201 | logger.log(_MISMATCH_LEVEL, message, key, issue_url, old, new)
202 |
203 |
204 | def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool:
205 | if old == new:
206 | return False
207 | _warn_mismatched(old, new, key=key)
208 | return True
209 |
210 |
211 | @functools.lru_cache(maxsize=None)
212 | def _log_context(
213 | *,
214 | user: bool = False,
215 | home: Optional[str] = None,
216 | root: Optional[str] = None,
217 | prefix: Optional[str] = None,
218 | ) -> None:
219 | parts = [
220 | "Additional context:",
221 | "user = %r",
222 | "home = %r",
223 | "root = %r",
224 | "prefix = %r",
225 | ]
226 |
227 | logger.log(_MISMATCH_LEVEL, "\n".join(parts), user, home, root, prefix)
228 |
229 |
230 | def get_scheme(
231 | dist_name: str,
232 | user: bool = False,
233 | home: Optional[str] = None,
234 | root: Optional[str] = None,
235 | isolated: bool = False,
236 | prefix: Optional[str] = None,
237 | ) -> Scheme:
238 | new = _sysconfig.get_scheme(
239 | dist_name,
240 | user=user,
241 | home=home,
242 | root=root,
243 | isolated=isolated,
244 | prefix=prefix,
245 | )
246 | if _USE_SYSCONFIG:
247 | return new
248 |
249 | old = _distutils.get_scheme(
250 | dist_name,
251 | user=user,
252 | home=home,
253 | root=root,
254 | isolated=isolated,
255 | prefix=prefix,
256 | )
257 |
258 | warning_contexts = []
259 | for k in SCHEME_KEYS:
260 | old_v = pathlib.Path(getattr(old, k))
261 | new_v = pathlib.Path(getattr(new, k))
262 |
263 | if old_v == new_v:
264 | continue
265 |
266 | # distutils incorrectly put PyPy packages under ``site-packages/python``
267 | # in the ``posix_home`` scheme, but PyPy devs said they expect the
268 | # directory name to be ``pypy`` instead. So we treat this as a bug fix
269 | # and not warn about it. See bpo-43307 and python/cpython#24628.
270 | skip_pypy_special_case = (
271 | sys.implementation.name == "pypy"
272 | and home is not None
273 | and k in ("platlib", "purelib")
274 | and old_v.parent == new_v.parent
275 | and old_v.name.startswith("python")
276 | and new_v.name.startswith("pypy")
277 | )
278 | if skip_pypy_special_case:
279 | continue
280 |
281 | # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in
282 | # the ``include`` value, but distutils's ``headers`` does. We'll let
283 | # CPython decide whether this is a bug or feature. See bpo-43948.
284 | skip_osx_framework_user_special_case = (
285 | user
286 | and is_osx_framework()
287 | and k == "headers"
288 | and old_v.parent.parent == new_v.parent
289 | and old_v.parent.name.startswith("python")
290 | )
291 | if skip_osx_framework_user_special_case:
292 | continue
293 |
294 | # On Red Hat and derived Linux distributions, distutils is patched to
295 | # use "lib64" instead of "lib" for platlib.
296 | if k == "platlib" and _looks_like_red_hat_lib():
297 | continue
298 |
299 | # On Python 3.9+, sysconfig's posix_user scheme sets platlib against
300 | # sys.platlibdir, but distutils's unix_user incorrectly coninutes
301 | # using the same $usersite for both platlib and purelib. This creates a
302 | # mismatch when sys.platlibdir is not "lib".
303 | skip_bpo_44860 = (
304 | user
305 | and k == "platlib"
306 | and not WINDOWS
307 | and sys.version_info >= (3, 9)
308 | and _PLATLIBDIR != "lib"
309 | and _looks_like_bpo_44860()
310 | )
311 | if skip_bpo_44860:
312 | continue
313 |
314 | # Slackware incorrectly patches posix_user to use lib64 instead of lib,
315 | # but not usersite to match the location.
316 | skip_slackware_user_scheme = (
317 | user
318 | and k in ("platlib", "purelib")
319 | and not WINDOWS
320 | and _looks_like_slackware_scheme()
321 | )
322 | if skip_slackware_user_scheme:
323 | continue
324 |
325 | # Both Debian and Red Hat patch Python to place the system site under
326 | # /usr/local instead of /usr. Debian also places lib in dist-packages
327 | # instead of site-packages, but the /usr/local check should cover it.
328 | skip_linux_system_special_case = (
329 | not (user or home or prefix or running_under_virtualenv())
330 | and old_v.parts[1:3] == ("usr", "local")
331 | and len(new_v.parts) > 1
332 | and new_v.parts[1] == "usr"
333 | and (len(new_v.parts) < 3 or new_v.parts[2] != "local")
334 | and (_looks_like_red_hat_scheme() or _looks_like_debian_scheme())
335 | )
336 | if skip_linux_system_special_case:
337 | continue
338 |
339 | # MSYS2 MINGW's sysconfig patch does not include the "site-packages"
340 | # part of the path. This is incorrect and will be fixed in MSYS.
341 | skip_msys2_mingw_bug = (
342 | WINDOWS and k in ("platlib", "purelib") and _looks_like_msys2_mingw_scheme()
343 | )
344 | if skip_msys2_mingw_bug:
345 | continue
346 |
347 | # CPython's POSIX install script invokes pip (via ensurepip) against the
348 | # interpreter located in the source tree, not the install site. This
349 | # triggers special logic in sysconfig that's not present in distutils.
350 | # https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194
351 | skip_cpython_build = (
352 | sysconfig.is_python_build(check_home=True)
353 | and not WINDOWS
354 | and k in ("headers", "include", "platinclude")
355 | )
356 | if skip_cpython_build:
357 | continue
358 |
359 | warning_contexts.append((old_v, new_v, f"scheme.{k}"))
360 |
361 | if not warning_contexts:
362 | return old
363 |
364 | # Check if this path mismatch is caused by distutils config files. Those
365 | # files will no longer work once we switch to sysconfig, so this raises a
366 | # deprecation message for them.
367 | default_old = _distutils.distutils_scheme(
368 | dist_name,
369 | user,
370 | home,
371 | root,
372 | isolated,
373 | prefix,
374 | ignore_config_files=True,
375 | )
376 | if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS):
377 | deprecated(
378 | reason=(
379 | "Configuring installation scheme with distutils config files "
380 | "is deprecated and will no longer work in the near future. If you "
381 | "are using a Homebrew or Linuxbrew Python, please see discussion "
382 | "at https://github.com/Homebrew/homebrew-core/issues/76621"
383 | ),
384 | replacement=None,
385 | gone_in=None,
386 | )
387 | return old
388 |
389 | # Post warnings about this mismatch so user can report them back.
390 | for old_v, new_v, key in warning_contexts:
391 | _warn_mismatched(old_v, new_v, key=key)
392 | _log_context(user=user, home=home, root=root, prefix=prefix)
393 |
394 | return old
395 |
396 |
397 | def get_bin_prefix() -> str:
398 | new = _sysconfig.get_bin_prefix()
399 | if _USE_SYSCONFIG:
400 | return new
401 |
402 | old = _distutils.get_bin_prefix()
403 | if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"):
404 | _log_context()
405 | return old
406 |
407 |
408 | def get_bin_user() -> str:
409 | return _sysconfig.get_scheme("", user=True).scripts
410 |
411 |
412 | def _looks_like_deb_system_dist_packages(value: str) -> bool:
413 | """Check if the value is Debian's APT-controlled dist-packages.
414 |
415 | Debian's ``distutils.sysconfig.get_python_lib()`` implementation returns the
416 | default package path controlled by APT, but does not patch ``sysconfig`` to
417 | do the same. This is similar to the bug worked around in ``get_scheme()``,
418 | but here the default is ``deb_system`` instead of ``unix_local``. Ultimately
419 | we can't do anything about this Debian bug, and this detection allows us to
420 | skip the warning when needed.
421 | """
422 | if not _looks_like_debian_scheme():
423 | return False
424 | if value == "/usr/lib/python3/dist-packages":
425 | return True
426 | return False
427 |
428 |
429 | def get_purelib() -> str:
430 | """Return the default pure-Python lib location."""
431 | new = _sysconfig.get_purelib()
432 | if _USE_SYSCONFIG:
433 | return new
434 |
435 | old = _distutils.get_purelib()
436 | if _looks_like_deb_system_dist_packages(old):
437 | return old
438 | if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"):
439 | _log_context()
440 | return old
441 |
442 |
443 | def get_platlib() -> str:
444 | """Return the default platform-shared lib location."""
445 | new = _sysconfig.get_platlib()
446 | if _USE_SYSCONFIG:
447 | return new
448 |
449 | from . import _distutils
450 |
451 | old = _distutils.get_platlib()
452 | if _looks_like_deb_system_dist_packages(old):
453 | return old
454 | if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"):
455 | _log_context()
456 | return old
457 |
```