This is page 62 of 168. Use http://codebase.md/romanshablio/mcp_server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .DS_Store
├── .venv
│ ├── __pycache__
│ │ └── hello.cpython-312.pyc
│ ├── bin
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── Activate.ps1
│ │ ├── flask
│ │ ├── normalizer
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.12
│ │ ├── python
│ │ ├── python3
│ │ └── python3.12
│ ├── hello.py
│ ├── lib
│ │ └── python3.12
│ │ └── site-packages
│ │ ├── beautifulsoup4-4.12.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ ├── AUTHORS
│ │ │ │ └── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── blinker
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _utilities.cpython-312.pyc
│ │ │ │ └── base.cpython-312.pyc
│ │ │ ├── _utilities.py
│ │ │ ├── base.py
│ │ │ └── py.typed
│ │ ├── blinker-1.8.2.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── bs4
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── css.cpython-312.pyc
│ │ │ │ ├── dammit.cpython-312.pyc
│ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ ├── element.cpython-312.pyc
│ │ │ │ └── formatter.cpython-312.pyc
│ │ │ ├── builder
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _html5lib.cpython-312.pyc
│ │ │ │ │ ├── _htmlparser.cpython-312.pyc
│ │ │ │ │ └── _lxml.cpython-312.pyc
│ │ │ │ ├── _html5lib.py
│ │ │ │ ├── _htmlparser.py
│ │ │ │ └── _lxml.py
│ │ │ ├── css.py
│ │ │ ├── dammit.py
│ │ │ ├── diagnose.py
│ │ │ ├── element.py
│ │ │ ├── formatter.py
│ │ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── test_builder_registry.cpython-312.pyc
│ │ │ │ ├── test_builder.cpython-312.pyc
│ │ │ │ ├── test_css.cpython-312.pyc
│ │ │ │ ├── test_dammit.cpython-312.pyc
│ │ │ │ ├── test_docs.cpython-312.pyc
│ │ │ │ ├── test_element.cpython-312.pyc
│ │ │ │ ├── test_formatter.cpython-312.pyc
│ │ │ │ ├── test_fuzz.cpython-312.pyc
│ │ │ │ ├── test_html5lib.cpython-312.pyc
│ │ │ │ ├── test_htmlparser.cpython-312.pyc
│ │ │ │ ├── test_lxml.cpython-312.pyc
│ │ │ │ ├── test_navigablestring.cpython-312.pyc
│ │ │ │ ├── test_pageelement.cpython-312.pyc
│ │ │ │ ├── test_soup.cpython-312.pyc
│ │ │ │ ├── test_tag.cpython-312.pyc
│ │ │ │ └── test_tree.cpython-312.pyc
│ │ │ ├── fuzz
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase
│ │ │ │ ├── crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase
│ │ │ │ └── crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase
│ │ │ ├── test_builder_registry.py
│ │ │ ├── test_builder.py
│ │ │ ├── test_css.py
│ │ │ ├── test_dammit.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_element.py
│ │ │ ├── test_formatter.py
│ │ │ ├── test_fuzz.py
│ │ │ ├── test_html5lib.py
│ │ │ ├── test_htmlparser.py
│ │ │ ├── test_lxml.py
│ │ │ ├── test_navigablestring.py
│ │ │ ├── test_pageelement.py
│ │ │ ├── test_soup.py
│ │ │ ├── test_tag.py
│ │ │ └── test_tree.py
│ │ ├── certifi
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── core.cpython-312.pyc
│ │ │ ├── cacert.pem
│ │ │ ├── core.py
│ │ │ └── py.typed
│ │ ├── certifi-2024.8.30.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── charset_normalizer
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── cd.cpython-312.pyc
│ │ │ │ ├── constant.cpython-312.pyc
│ │ │ │ ├── legacy.cpython-312.pyc
│ │ │ │ ├── md.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── version.cpython-312.pyc
│ │ │ ├── api.py
│ │ │ ├── cd.py
│ │ │ ├── cli
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── __main__.cpython-312.pyc
│ │ │ ├── constant.py
│ │ │ ├── legacy.py
│ │ │ ├── md__mypyc.cpython-312-darwin.so
│ │ │ ├── md.cpython-312-darwin.so
│ │ │ ├── md.py
│ │ │ ├── models.py
│ │ │ ├── py.typed
│ │ │ ├── utils.py
│ │ │ └── version.py
│ │ ├── charset_normalizer-3.4.0.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── click
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ ├── _termui_impl.cpython-312.pyc
│ │ │ │ ├── _textwrap.cpython-312.pyc
│ │ │ │ ├── _winconsole.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── decorators.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formatting.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── shell_completion.cpython-312.pyc
│ │ │ │ ├── termui.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── types.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── _compat.py
│ │ │ ├── _termui_impl.py
│ │ │ ├── _textwrap.py
│ │ │ ├── _winconsole.py
│ │ │ ├── core.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── formatting.py
│ │ │ ├── globals.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── shell_completion.py
│ │ │ ├── termui.py
│ │ │ ├── testing.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ ├── click-8.1.7.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.rst
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── fake_useragent
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ ├── fake.cpython-312.pyc
│ │ │ │ ├── log.cpython-312.pyc
│ │ │ │ ├── settings.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── data
│ │ │ │ └── browsers.json
│ │ │ ├── errors.py
│ │ │ ├── fake.py
│ │ │ ├── log.py
│ │ │ ├── settings.py
│ │ │ └── utils.py
│ │ ├── fake_useragent-1.5.1.dist-info
│ │ │ ├── AUTHORS
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── flask
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ ├── cli.cpython-312.pyc
│ │ │ │ ├── config.cpython-312.pyc
│ │ │ │ ├── ctx.cpython-312.pyc
│ │ │ │ ├── debughelpers.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── helpers.cpython-312.pyc
│ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── signals.cpython-312.pyc
│ │ │ │ ├── templating.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── typing.cpython-312.pyc
│ │ │ │ ├── views.cpython-312.pyc
│ │ │ │ └── wrappers.cpython-312.pyc
│ │ │ ├── app.py
│ │ │ ├── blueprints.py
│ │ │ ├── cli.py
│ │ │ ├── config.py
│ │ │ ├── ctx.py
│ │ │ ├── debughelpers.py
│ │ │ ├── globals.py
│ │ │ ├── helpers.py
│ │ │ ├── json
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ └── tag.cpython-312.pyc
│ │ │ │ ├── provider.py
│ │ │ │ └── tag.py
│ │ │ ├── logging.py
│ │ │ ├── py.typed
│ │ │ ├── sansio
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ │ └── scaffold.cpython-312.pyc
│ │ │ │ ├── app.py
│ │ │ │ ├── blueprints.py
│ │ │ │ ├── README.md
│ │ │ │ └── scaffold.py
│ │ │ ├── sessions.py
│ │ │ ├── signals.py
│ │ │ ├── templating.py
│ │ │ ├── testing.py
│ │ │ ├── typing.py
│ │ │ ├── views.py
│ │ │ └── wrappers.py
│ │ ├── flask-3.0.3.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── idna
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ ├── codec.py
│ │ │ ├── compat.py
│ │ │ ├── core.py
│ │ │ ├── idnadata.py
│ │ │ ├── intranges.py
│ │ │ ├── package_data.py
│ │ │ ├── py.typed
│ │ │ └── uts46data.py
│ │ ├── idna-3.10.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── itsdangerous
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ ├── exc.cpython-312.pyc
│ │ │ │ ├── serializer.cpython-312.pyc
│ │ │ │ ├── signer.cpython-312.pyc
│ │ │ │ ├── timed.cpython-312.pyc
│ │ │ │ └── url_safe.cpython-312.pyc
│ │ │ ├── _json.py
│ │ │ ├── encoding.py
│ │ │ ├── exc.py
│ │ │ ├── py.typed
│ │ │ ├── serializer.py
│ │ │ ├── signer.py
│ │ │ ├── timed.py
│ │ │ └── url_safe.py
│ │ ├── itsdangerous-2.2.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── jinja2
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _identifier.cpython-312.pyc
│ │ │ │ ├── async_utils.cpython-312.pyc
│ │ │ │ ├── bccache.cpython-312.pyc
│ │ │ │ ├── compiler.cpython-312.pyc
│ │ │ │ ├── constants.cpython-312.pyc
│ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ ├── defaults.cpython-312.pyc
│ │ │ │ ├── environment.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ ├── filters.cpython-312.pyc
│ │ │ │ ├── idtracking.cpython-312.pyc
│ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ ├── loaders.cpython-312.pyc
│ │ │ │ ├── meta.cpython-312.pyc
│ │ │ │ ├── nativetypes.cpython-312.pyc
│ │ │ │ ├── nodes.cpython-312.pyc
│ │ │ │ ├── optimizer.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── runtime.cpython-312.pyc
│ │ │ │ ├── sandbox.cpython-312.pyc
│ │ │ │ ├── tests.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── visitor.cpython-312.pyc
│ │ │ ├── _identifier.py
│ │ │ ├── async_utils.py
│ │ │ ├── bccache.py
│ │ │ ├── compiler.py
│ │ │ ├── constants.py
│ │ │ ├── debug.py
│ │ │ ├── defaults.py
│ │ │ ├── environment.py
│ │ │ ├── exceptions.py
│ │ │ ├── ext.py
│ │ │ ├── filters.py
│ │ │ ├── idtracking.py
│ │ │ ├── lexer.py
│ │ │ ├── loaders.py
│ │ │ ├── meta.py
│ │ │ ├── nativetypes.py
│ │ │ ├── nodes.py
│ │ │ ├── optimizer.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── runtime.py
│ │ │ ├── sandbox.py
│ │ │ ├── tests.py
│ │ │ ├── utils.py
│ │ │ └── visitor.py
│ │ ├── jinja2-3.1.4.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── lxml
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _elementpath.cpython-312.pyc
│ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ ├── cssselect.cpython-312.pyc
│ │ │ │ ├── doctestcompare.cpython-312.pyc
│ │ │ │ ├── ElementInclude.cpython-312.pyc
│ │ │ │ ├── pyclasslookup.cpython-312.pyc
│ │ │ │ ├── sax.cpython-312.pyc
│ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ ├── _elementpath.cpython-312-darwin.so
│ │ │ ├── _elementpath.py
│ │ │ ├── apihelpers.pxi
│ │ │ ├── builder.cpython-312-darwin.so
│ │ │ ├── builder.py
│ │ │ ├── classlookup.pxi
│ │ │ ├── cleanup.pxi
│ │ │ ├── cssselect.py
│ │ │ ├── debug.pxi
│ │ │ ├── docloader.pxi
│ │ │ ├── doctestcompare.py
│ │ │ ├── dtd.pxi
│ │ │ ├── ElementInclude.py
│ │ │ ├── etree_api.h
│ │ │ ├── etree.cpython-312-darwin.so
│ │ │ ├── etree.h
│ │ │ ├── etree.pyx
│ │ │ ├── extensions.pxi
│ │ │ ├── html
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _diffcommand.cpython-312.pyc
│ │ │ │ │ ├── _html5builder.cpython-312.pyc
│ │ │ │ │ ├── _setmixin.cpython-312.pyc
│ │ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ │ ├── clean.cpython-312.pyc
│ │ │ │ │ ├── defs.cpython-312.pyc
│ │ │ │ │ ├── diff.cpython-312.pyc
│ │ │ │ │ ├── ElementSoup.cpython-312.pyc
│ │ │ │ │ ├── formfill.cpython-312.pyc
│ │ │ │ │ ├── html5parser.cpython-312.pyc
│ │ │ │ │ ├── soupparser.cpython-312.pyc
│ │ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ │ ├── _diffcommand.py
│ │ │ │ ├── _html5builder.py
│ │ │ │ ├── _setmixin.py
│ │ │ │ ├── builder.py
│ │ │ │ ├── clean.py
│ │ │ │ ├── defs.py
│ │ │ │ ├── diff.cpython-312-darwin.so
│ │ │ │ ├── diff.py
│ │ │ │ ├── ElementSoup.py
│ │ │ │ ├── formfill.py
│ │ │ │ ├── html5parser.py
│ │ │ │ ├── soupparser.py
│ │ │ │ └── usedoctest.py
│ │ │ ├── includes
│ │ │ │ ├── __init__.pxd
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── c14n.pxd
│ │ │ │ ├── config.pxd
│ │ │ │ ├── dtdvalid.pxd
│ │ │ │ ├── etree_defs.h
│ │ │ │ ├── etreepublic.pxd
│ │ │ │ ├── extlibs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── libcharset.h
│ │ │ │ │ ├── localcharset.h
│ │ │ │ │ ├── zconf.h
│ │ │ │ │ └── zlib.h
│ │ │ │ ├── htmlparser.pxd
│ │ │ │ ├── libexslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── exslt.h
│ │ │ │ │ ├── exsltconfig.h
│ │ │ │ │ └── exsltexports.h
│ │ │ │ ├── libxml
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── c14n.h
│ │ │ │ │ ├── catalog.h
│ │ │ │ │ ├── chvalid.h
│ │ │ │ │ ├── debugXML.h
│ │ │ │ │ ├── dict.h
│ │ │ │ │ ├── encoding.h
│ │ │ │ │ ├── entities.h
│ │ │ │ │ ├── globals.h
│ │ │ │ │ ├── hash.h
│ │ │ │ │ ├── HTMLparser.h
│ │ │ │ │ ├── HTMLtree.h
│ │ │ │ │ ├── list.h
│ │ │ │ │ ├── nanoftp.h
│ │ │ │ │ ├── nanohttp.h
│ │ │ │ │ ├── parser.h
│ │ │ │ │ ├── parserInternals.h
│ │ │ │ │ ├── relaxng.h
│ │ │ │ │ ├── SAX.h
│ │ │ │ │ ├── SAX2.h
│ │ │ │ │ ├── schemasInternals.h
│ │ │ │ │ ├── schematron.h
│ │ │ │ │ ├── threads.h
│ │ │ │ │ ├── tree.h
│ │ │ │ │ ├── uri.h
│ │ │ │ │ ├── valid.h
│ │ │ │ │ ├── xinclude.h
│ │ │ │ │ ├── xlink.h
│ │ │ │ │ ├── xmlautomata.h
│ │ │ │ │ ├── xmlerror.h
│ │ │ │ │ ├── xmlexports.h
│ │ │ │ │ ├── xmlIO.h
│ │ │ │ │ ├── xmlmemory.h
│ │ │ │ │ ├── xmlmodule.h
│ │ │ │ │ ├── xmlreader.h
│ │ │ │ │ ├── xmlregexp.h
│ │ │ │ │ ├── xmlsave.h
│ │ │ │ │ ├── xmlschemas.h
│ │ │ │ │ ├── xmlschemastypes.h
│ │ │ │ │ ├── xmlstring.h
│ │ │ │ │ ├── xmlunicode.h
│ │ │ │ │ ├── xmlversion.h
│ │ │ │ │ ├── xmlwriter.h
│ │ │ │ │ ├── xpath.h
│ │ │ │ │ ├── xpathInternals.h
│ │ │ │ │ └── xpointer.h
│ │ │ │ ├── libxslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── attributes.h
│ │ │ │ │ ├── documents.h
│ │ │ │ │ ├── extensions.h
│ │ │ │ │ ├── extra.h
│ │ │ │ │ ├── functions.h
│ │ │ │ │ ├── imports.h
│ │ │ │ │ ├── keys.h
│ │ │ │ │ ├── namespaces.h
│ │ │ │ │ ├── numbersInternals.h
│ │ │ │ │ ├── pattern.h
│ │ │ │ │ ├── preproc.h
│ │ │ │ │ ├── security.h
│ │ │ │ │ ├── templates.h
│ │ │ │ │ ├── transform.h
│ │ │ │ │ ├── variables.h
│ │ │ │ │ ├── xslt.h
│ │ │ │ │ ├── xsltconfig.h
│ │ │ │ │ ├── xsltexports.h
│ │ │ │ │ ├── xsltInternals.h
│ │ │ │ │ ├── xsltlocale.h
│ │ │ │ │ └── xsltutils.h
│ │ │ │ ├── lxml-version.h
│ │ │ │ ├── relaxng.pxd
│ │ │ │ ├── schematron.pxd
│ │ │ │ ├── tree.pxd
│ │ │ │ ├── uri.pxd
│ │ │ │ ├── xinclude.pxd
│ │ │ │ ├── xmlerror.pxd
│ │ │ │ ├── xmlparser.pxd
│ │ │ │ ├── xmlschema.pxd
│ │ │ │ ├── xpath.pxd
│ │ │ │ └── xslt.pxd
│ │ │ ├── isoschematron
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ └── resources
│ │ │ │ ├── rng
│ │ │ │ │ └── iso-schematron.rng
│ │ │ │ └── xsl
│ │ │ │ ├── iso-schematron-xslt1
│ │ │ │ │ ├── iso_abstract_expand.xsl
│ │ │ │ │ ├── iso_dsdl_include.xsl
│ │ │ │ │ ├── iso_schematron_message.xsl
│ │ │ │ │ ├── iso_schematron_skeleton_for_xslt1.xsl
│ │ │ │ │ ├── iso_svrl_for_xslt1.xsl
│ │ │ │ │ └── readme.txt
│ │ │ │ ├── RNG2Schtrn.xsl
│ │ │ │ └── XSD2Schtrn.xsl
│ │ │ ├── iterparse.pxi
│ │ │ ├── lxml.etree_api.h
│ │ │ ├── lxml.etree.h
│ │ │ ├── nsclasses.pxi
│ │ │ ├── objectify.cpython-312-darwin.so
│ │ │ ├── objectify.pyx
│ │ │ ├── objectpath.pxi
│ │ │ ├── parser.pxi
│ │ │ ├── parsertarget.pxi
│ │ │ ├── proxy.pxi
│ │ │ ├── public-api.pxi
│ │ │ ├── pyclasslookup.py
│ │ │ ├── readonlytree.pxi
│ │ │ ├── relaxng.pxi
│ │ │ ├── sax.cpython-312-darwin.so
│ │ │ ├── sax.py
│ │ │ ├── saxparser.pxi
│ │ │ ├── schematron.pxi
│ │ │ ├── serializer.pxi
│ │ │ ├── usedoctest.py
│ │ │ ├── xinclude.pxi
│ │ │ ├── xmlerror.pxi
│ │ │ ├── xmlid.pxi
│ │ │ ├── xmlschema.pxi
│ │ │ ├── xpath.pxi
│ │ │ ├── xslt.pxi
│ │ │ └── xsltext.pxi
│ │ ├── lxml-5.3.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── LICENSES.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── markupsafe
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── _native.cpython-312.pyc
│ │ │ ├── _native.py
│ │ │ ├── _speedups.c
│ │ │ ├── _speedups.cpython-312-darwin.so
│ │ │ ├── _speedups.pyi
│ │ │ └── py.typed
│ │ ├── MarkupSafe-3.0.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── pip
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pip-runner__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── __pip-runner__.cpython-312.pyc
│ │ │ ├── _internal
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── build_env.cpython-312.pyc
│ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ ├── pyproject.cpython-312.pyc
│ │ │ │ │ ├── self_outdated_check.cpython-312.pyc
│ │ │ │ │ └── wheel_builder.cpython-312.pyc
│ │ │ │ ├── build_env.py
│ │ │ │ ├── cache.py
│ │ │ │ ├── cli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── autocompletion.cpython-312.pyc
│ │ │ │ │ │ ├── base_command.cpython-312.pyc
│ │ │ │ │ │ ├── cmdoptions.cpython-312.pyc
│ │ │ │ │ │ ├── command_context.cpython-312.pyc
│ │ │ │ │ │ ├── index_command.cpython-312.pyc
│ │ │ │ │ │ ├── main_parser.cpython-312.pyc
│ │ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bars.cpython-312.pyc
│ │ │ │ │ │ ├── req_command.cpython-312.pyc
│ │ │ │ │ │ ├── spinners.cpython-312.pyc
│ │ │ │ │ │ └── status_codes.cpython-312.pyc
│ │ │ │ │ ├── autocompletion.py
│ │ │ │ │ ├── base_command.py
│ │ │ │ │ ├── cmdoptions.py
│ │ │ │ │ ├── command_context.py
│ │ │ │ │ ├── index_command.py
│ │ │ │ │ ├── main_parser.py
│ │ │ │ │ ├── main.py
│ │ │ │ │ ├── parser.py
│ │ │ │ │ ├── progress_bars.py
│ │ │ │ │ ├── req_command.py
│ │ │ │ │ ├── spinners.py
│ │ │ │ │ └── status_codes.py
│ │ │ │ ├── commands
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── completion.cpython-312.pyc
│ │ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ ├── hash.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── inspect.cpython-312.pyc
│ │ │ │ │ │ ├── install.cpython-312.pyc
│ │ │ │ │ │ ├── list.cpython-312.pyc
│ │ │ │ │ │ ├── search.cpython-312.pyc
│ │ │ │ │ │ ├── show.cpython-312.pyc
│ │ │ │ │ │ ├── uninstall.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── completion.py
│ │ │ │ │ ├── configuration.py
│ │ │ │ │ ├── debug.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── hash.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── inspect.py
│ │ │ │ │ ├── install.py
│ │ │ │ │ ├── list.py
│ │ │ │ │ ├── search.py
│ │ │ │ │ ├── show.py
│ │ │ │ │ ├── uninstall.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── configuration.py
│ │ │ │ ├── distributions
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── installed.cpython-312.pyc
│ │ │ │ │ │ ├── sdist.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── installed.py
│ │ │ │ │ ├── sdist.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── index
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── collector.cpython-312.pyc
│ │ │ │ │ │ ├── package_finder.cpython-312.pyc
│ │ │ │ │ │ └── sources.cpython-312.pyc
│ │ │ │ │ ├── collector.py
│ │ │ │ │ ├── package_finder.py
│ │ │ │ │ └── sources.py
│ │ │ │ ├── locations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _distutils.cpython-312.pyc
│ │ │ │ │ │ ├── _sysconfig.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── _distutils.py
│ │ │ │ │ ├── _sysconfig.py
│ │ │ │ │ └── base.py
│ │ │ │ ├── main.py
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ └── pkg_resources.cpython-312.pyc
│ │ │ │ │ ├── _json.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── importlib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ │ ├── _dists.cpython-312.pyc
│ │ │ │ │ │ │ └── _envs.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.py
│ │ │ │ │ │ ├── _dists.py
│ │ │ │ │ │ └── _envs.py
│ │ │ │ │ └── pkg_resources.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── candidate.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url.cpython-312.pyc
│ │ │ │ │ │ ├── format_control.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── installation_report.cpython-312.pyc
│ │ │ │ │ │ ├── link.cpython-312.pyc
│ │ │ │ │ │ ├── scheme.cpython-312.pyc
│ │ │ │ │ │ ├── search_scope.cpython-312.pyc
│ │ │ │ │ │ ├── selection_prefs.cpython-312.pyc
│ │ │ │ │ │ ├── target_python.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── candidate.py
│ │ │ │ │ ├── direct_url.py
│ │ │ │ │ ├── format_control.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── installation_report.py
│ │ │ │ │ ├── link.py
│ │ │ │ │ ├── scheme.py
│ │ │ │ │ ├── search_scope.py
│ │ │ │ │ ├── selection_prefs.py
│ │ │ │ │ ├── target_python.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── network
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── lazy_wheel.cpython-312.pyc
│ │ │ │ │ │ ├── session.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── xmlrpc.cpython-312.pyc
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── lazy_wheel.py
│ │ │ │ │ ├── session.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── xmlrpc.py
│ │ │ │ ├── operations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ └── prepare.cpython-312.pyc
│ │ │ │ │ ├── build
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── build_tracker.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_legacy.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── build_tracker.py
│ │ │ │ │ │ ├── metadata_editable.py
│ │ │ │ │ │ ├── metadata_legacy.py
│ │ │ │ │ │ ├── metadata.py
│ │ │ │ │ │ ├── wheel_editable.py
│ │ │ │ │ │ ├── wheel_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── install
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── editable_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── editable_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ └── prepare.py
│ │ │ │ ├── pyproject.py
│ │ │ │ ├── req
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── constructors.cpython-312.pyc
│ │ │ │ │ │ ├── req_file.cpython-312.pyc
│ │ │ │ │ │ ├── req_install.cpython-312.pyc
│ │ │ │ │ │ ├── req_set.cpython-312.pyc
│ │ │ │ │ │ └── req_uninstall.cpython-312.pyc
│ │ │ │ │ ├── constructors.py
│ │ │ │ │ ├── req_file.py
│ │ │ │ │ ├── req_install.py
│ │ │ │ │ ├── req_set.py
│ │ │ │ │ └── req_uninstall.py
│ │ │ │ ├── resolution
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ │ └── resolver.py
│ │ │ │ │ └── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── candidates.cpython-312.pyc
│ │ │ │ │ │ ├── factory.cpython-312.pyc
│ │ │ │ │ │ ├── found_candidates.cpython-312.pyc
│ │ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ │ ├── reporter.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── candidates.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── found_candidates.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ ├── reporter.py
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ └── resolver.py
│ │ │ │ ├── self_outdated_check.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _jaraco_text.cpython-312.pyc
│ │ │ │ │ │ ├── _log.cpython-312.pyc
│ │ │ │ │ │ ├── appdirs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── compatibility_tags.cpython-312.pyc
│ │ │ │ │ │ ├── datetime.cpython-312.pyc
│ │ │ │ │ │ ├── deprecation.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url_helpers.cpython-312.pyc
│ │ │ │ │ │ ├── egg_link.cpython-312.pyc
│ │ │ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ │ │ ├── entrypoints.cpython-312.pyc
│ │ │ │ │ │ ├── filesystem.cpython-312.pyc
│ │ │ │ │ │ ├── filetypes.cpython-312.pyc
│ │ │ │ │ │ ├── glibc.cpython-312.pyc
│ │ │ │ │ │ ├── hashes.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── misc.cpython-312.pyc
│ │ │ │ │ │ ├── packaging.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── setuptools_build.cpython-312.pyc
│ │ │ │ │ │ ├── subprocess.cpython-312.pyc
│ │ │ │ │ │ ├── temp_dir.cpython-312.pyc
│ │ │ │ │ │ ├── unpacking.cpython-312.pyc
│ │ │ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ │ │ ├── virtualenv.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── _jaraco_text.py
│ │ │ │ │ ├── _log.py
│ │ │ │ │ ├── appdirs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── compatibility_tags.py
│ │ │ │ │ ├── datetime.py
│ │ │ │ │ ├── deprecation.py
│ │ │ │ │ ├── direct_url_helpers.py
│ │ │ │ │ ├── egg_link.py
│ │ │ │ │ ├── encoding.py
│ │ │ │ │ ├── entrypoints.py
│ │ │ │ │ ├── filesystem.py
│ │ │ │ │ ├── filetypes.py
│ │ │ │ │ ├── glibc.py
│ │ │ │ │ ├── hashes.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── misc.py
│ │ │ │ │ ├── packaging.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── setuptools_build.py
│ │ │ │ │ ├── subprocess.py
│ │ │ │ │ ├── temp_dir.py
│ │ │ │ │ ├── unpacking.py
│ │ │ │ │ ├── urls.py
│ │ │ │ │ ├── virtualenv.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── vcs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── bazaar.cpython-312.pyc
│ │ │ │ │ │ ├── git.cpython-312.pyc
│ │ │ │ │ │ ├── mercurial.cpython-312.pyc
│ │ │ │ │ │ ├── subversion.cpython-312.pyc
│ │ │ │ │ │ └── versioncontrol.cpython-312.pyc
│ │ │ │ │ ├── bazaar.py
│ │ │ │ │ ├── git.py
│ │ │ │ │ ├── mercurial.py
│ │ │ │ │ ├── subversion.py
│ │ │ │ │ └── versioncontrol.py
│ │ │ │ └── wheel_builder.py
│ │ │ ├── _vendor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ └── typing_extensions.cpython-312.pyc
│ │ │ │ ├── cachecontrol
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _cmd.cpython-312.pyc
│ │ │ │ │ │ ├── adapter.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── controller.cpython-312.pyc
│ │ │ │ │ │ ├── filewrapper.cpython-312.pyc
│ │ │ │ │ │ ├── heuristics.cpython-312.pyc
│ │ │ │ │ │ ├── serialize.cpython-312.pyc
│ │ │ │ │ │ └── wrapper.cpython-312.pyc
│ │ │ │ │ ├── _cmd.py
│ │ │ │ │ ├── adapter.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── caches
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── file_cache.cpython-312.pyc
│ │ │ │ │ │ │ └── redis_cache.cpython-312.pyc
│ │ │ │ │ │ ├── file_cache.py
│ │ │ │ │ │ └── redis_cache.py
│ │ │ │ │ ├── controller.py
│ │ │ │ │ ├── filewrapper.py
│ │ │ │ │ ├── heuristics.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── serialize.py
│ │ │ │ │ └── wrapper.py
│ │ │ │ ├── certifi
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── core.cpython-312.pyc
│ │ │ │ │ ├── cacert.pem
│ │ │ │ │ ├── core.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── distlib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── database.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── locators.cpython-312.pyc
│ │ │ │ │ │ ├── manifest.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── resources.cpython-312.pyc
│ │ │ │ │ │ ├── scripts.cpython-312.pyc
│ │ │ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── database.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── locators.py
│ │ │ │ │ ├── manifest.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ ├── scripts.py
│ │ │ │ │ ├── t32.exe
│ │ │ │ │ ├── t64-arm.exe
│ │ │ │ │ ├── t64.exe
│ │ │ │ │ ├── util.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ ├── w32.exe
│ │ │ │ │ ├── w64-arm.exe
│ │ │ │ │ ├── w64.exe
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── distro
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── distro.cpython-312.pyc
│ │ │ │ │ ├── distro.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── idna
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ │ │ ├── codec.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── core.py
│ │ │ │ │ ├── idnadata.py
│ │ │ │ │ ├── intranges.py
│ │ │ │ │ ├── package_data.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ └── uts46data.py
│ │ │ │ ├── msgpack
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ │ │ └── fallback.cpython-312.pyc
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── ext.py
│ │ │ │ │ └── fallback.py
│ │ │ │ ├── packaging
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _elffile.cpython-312.pyc
│ │ │ │ │ │ ├── _manylinux.cpython-312.pyc
│ │ │ │ │ │ ├── _musllinux.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _structures.cpython-312.pyc
│ │ │ │ │ │ ├── _tokenizer.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ ├── specifiers.cpython-312.pyc
│ │ │ │ │ │ ├── tags.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── version.cpython-312.pyc
│ │ │ │ │ ├── _elffile.py
│ │ │ │ │ ├── _manylinux.py
│ │ │ │ │ ├── _musllinux.py
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _structures.py
│ │ │ │ │ ├── _tokenizer.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ ├── specifiers.py
│ │ │ │ │ ├── tags.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── version.py
│ │ │ │ ├── pkg_resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── platformdirs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── android.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── macos.cpython-312.pyc
│ │ │ │ │ │ ├── unix.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── windows.cpython-312.pyc
│ │ │ │ │ ├── android.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── unix.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── pygments
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── cmdline.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── filter.cpython-312.pyc
│ │ │ │ │ │ ├── formatter.cpython-312.pyc
│ │ │ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ │ │ ├── modeline.cpython-312.pyc
│ │ │ │ │ │ ├── plugin.cpython-312.pyc
│ │ │ │ │ │ ├── regexopt.cpython-312.pyc
│ │ │ │ │ │ ├── scanner.cpython-312.pyc
│ │ │ │ │ │ ├── sphinxext.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── token.cpython-312.pyc
│ │ │ │ │ │ ├── unistring.cpython-312.pyc
│ │ │ │ │ │ └── util.cpython-312.pyc
│ │ │ │ │ ├── cmdline.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── filter.py
│ │ │ │ │ ├── filters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── formatter.py
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ ├── bbcode.cpython-312.pyc
│ │ │ │ │ │ │ ├── groff.cpython-312.pyc
│ │ │ │ │ │ │ ├── html.cpython-312.pyc
│ │ │ │ │ │ │ ├── img.cpython-312.pyc
│ │ │ │ │ │ │ ├── irc.cpython-312.pyc
│ │ │ │ │ │ │ ├── latex.cpython-312.pyc
│ │ │ │ │ │ │ ├── other.cpython-312.pyc
│ │ │ │ │ │ │ ├── pangomarkup.cpython-312.pyc
│ │ │ │ │ │ │ ├── rtf.cpython-312.pyc
│ │ │ │ │ │ │ ├── svg.cpython-312.pyc
│ │ │ │ │ │ │ ├── terminal.cpython-312.pyc
│ │ │ │ │ │ │ └── terminal256.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ ├── bbcode.py
│ │ │ │ │ │ ├── groff.py
│ │ │ │ │ │ ├── html.py
│ │ │ │ │ │ ├── img.py
│ │ │ │ │ │ ├── irc.py
│ │ │ │ │ │ ├── latex.py
│ │ │ │ │ │ ├── other.py
│ │ │ │ │ │ ├── pangomarkup.py
│ │ │ │ │ │ ├── rtf.py
│ │ │ │ │ │ ├── svg.py
│ │ │ │ │ │ ├── terminal.py
│ │ │ │ │ │ └── terminal256.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ ├── lexers
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ └── python.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ └── python.py
│ │ │ │ │ ├── modeline.py
│ │ │ │ │ ├── plugin.py
│ │ │ │ │ ├── regexopt.py
│ │ │ │ │ ├── scanner.py
│ │ │ │ │ ├── sphinxext.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styles
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── _mapping.cpython-312.pyc
│ │ │ │ │ │ └── _mapping.py
│ │ │ │ │ ├── token.py
│ │ │ │ │ ├── unistring.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── pyproject_hooks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ └── _impl.cpython-312.pyc
│ │ │ │ │ ├── _compat.py
│ │ │ │ │ ├── _impl.py
│ │ │ │ │ └── _in_process
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── _in_process.cpython-312.pyc
│ │ │ │ │ └── _in_process.py
│ │ │ │ ├── requests
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ │ ├── __version__.py
│ │ │ │ │ ├── _internal_utils.py
│ │ │ │ │ ├── adapters.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── certs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── cookies.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── hooks.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ ├── packages.py
│ │ │ │ │ ├── sessions.py
│ │ │ │ │ ├── status_codes.py
│ │ │ │ │ ├── structures.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── providers.cpython-312.pyc
│ │ │ │ │ │ ├── reporters.cpython-312.pyc
│ │ │ │ │ │ ├── resolvers.cpython-312.pyc
│ │ │ │ │ │ └── structs.cpython-312.pyc
│ │ │ │ │ ├── compat
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── collections_abc.cpython-312.pyc
│ │ │ │ │ │ └── collections_abc.py
│ │ │ │ │ ├── providers.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── reporters.py
│ │ │ │ │ ├── resolvers.py
│ │ │ │ │ └── structs.py
│ │ │ │ ├── rich
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── _cell_widths.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_codes.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_replace.cpython-312.pyc
│ │ │ │ │ │ ├── _export_format.cpython-312.pyc
│ │ │ │ │ │ ├── _extension.cpython-312.pyc
│ │ │ │ │ │ ├── _fileno.cpython-312.pyc
│ │ │ │ │ │ ├── _inspect.cpython-312.pyc
│ │ │ │ │ │ ├── _log_render.cpython-312.pyc
│ │ │ │ │ │ ├── _loop.cpython-312.pyc
│ │ │ │ │ │ ├── _null_file.cpython-312.pyc
│ │ │ │ │ │ ├── _palettes.cpython-312.pyc
│ │ │ │ │ │ ├── _pick.cpython-312.pyc
│ │ │ │ │ │ ├── _ratio.cpython-312.pyc
│ │ │ │ │ │ ├── _spinners.cpython-312.pyc
│ │ │ │ │ │ ├── _stack.cpython-312.pyc
│ │ │ │ │ │ ├── _timer.cpython-312.pyc
│ │ │ │ │ │ ├── _win32_console.cpython-312.pyc
│ │ │ │ │ │ ├── _windows_renderer.cpython-312.pyc
│ │ │ │ │ │ ├── _windows.cpython-312.pyc
│ │ │ │ │ │ ├── _wrap.cpython-312.pyc
│ │ │ │ │ │ ├── abc.cpython-312.pyc
│ │ │ │ │ │ ├── align.cpython-312.pyc
│ │ │ │ │ │ ├── ansi.cpython-312.pyc
│ │ │ │ │ │ ├── bar.cpython-312.pyc
│ │ │ │ │ │ ├── box.cpython-312.pyc
│ │ │ │ │ │ ├── cells.cpython-312.pyc
│ │ │ │ │ │ ├── color_triplet.cpython-312.pyc
│ │ │ │ │ │ ├── color.cpython-312.pyc
│ │ │ │ │ │ ├── columns.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── constrain.cpython-312.pyc
│ │ │ │ │ │ ├── containers.cpython-312.pyc
│ │ │ │ │ │ ├── control.cpython-312.pyc
│ │ │ │ │ │ ├── default_styles.cpython-312.pyc
│ │ │ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ │ │ ├── emoji.cpython-312.pyc
│ │ │ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ │ │ ├── file_proxy.cpython-312.pyc
│ │ │ │ │ │ ├── filesize.cpython-312.pyc
│ │ │ │ │ │ ├── highlighter.cpython-312.pyc
│ │ │ │ │ │ ├── json.cpython-312.pyc
│ │ │ │ │ │ ├── jupyter.cpython-312.pyc
│ │ │ │ │ │ ├── layout.cpython-312.pyc
│ │ │ │ │ │ ├── live_render.cpython-312.pyc
│ │ │ │ │ │ ├── live.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── markup.cpython-312.pyc
│ │ │ │ │ │ ├── measure.cpython-312.pyc
│ │ │ │ │ │ ├── padding.cpython-312.pyc
│ │ │ │ │ │ ├── pager.cpython-312.pyc
│ │ │ │ │ │ ├── palette.cpython-312.pyc
│ │ │ │ │ │ ├── panel.cpython-312.pyc
│ │ │ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bar.cpython-312.pyc
│ │ │ │ │ │ ├── progress.cpython-312.pyc
│ │ │ │ │ │ ├── prompt.cpython-312.pyc
│ │ │ │ │ │ ├── protocol.cpython-312.pyc
│ │ │ │ │ │ ├── region.cpython-312.pyc
│ │ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ │ ├── rule.cpython-312.pyc
│ │ │ │ │ │ ├── scope.cpython-312.pyc
│ │ │ │ │ │ ├── screen.cpython-312.pyc
│ │ │ │ │ │ ├── segment.cpython-312.pyc
│ │ │ │ │ │ ├── spinner.cpython-312.pyc
│ │ │ │ │ │ ├── status.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── styled.cpython-312.pyc
│ │ │ │ │ │ ├── syntax.cpython-312.pyc
│ │ │ │ │ │ ├── table.cpython-312.pyc
│ │ │ │ │ │ ├── terminal_theme.cpython-312.pyc
│ │ │ │ │ │ ├── text.cpython-312.pyc
│ │ │ │ │ │ ├── theme.cpython-312.pyc
│ │ │ │ │ │ ├── themes.cpython-312.pyc
│ │ │ │ │ │ ├── traceback.cpython-312.pyc
│ │ │ │ │ │ └── tree.cpython-312.pyc
│ │ │ │ │ ├── _cell_widths.py
│ │ │ │ │ ├── _emoji_codes.py
│ │ │ │ │ ├── _emoji_replace.py
│ │ │ │ │ ├── _export_format.py
│ │ │ │ │ ├── _extension.py
│ │ │ │ │ ├── _fileno.py
│ │ │ │ │ ├── _inspect.py
│ │ │ │ │ ├── _log_render.py
│ │ │ │ │ ├── _loop.py
│ │ │ │ │ ├── _null_file.py
│ │ │ │ │ ├── _palettes.py
│ │ │ │ │ ├── _pick.py
│ │ │ │ │ ├── _ratio.py
│ │ │ │ │ ├── _spinners.py
│ │ │ │ │ ├── _stack.py
│ │ │ │ │ ├── _timer.py
│ │ │ │ │ ├── _win32_console.py
│ │ │ │ │ ├── _windows_renderer.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ ├── _wrap.py
│ │ │ │ │ ├── abc.py
│ │ │ │ │ ├── align.py
│ │ │ │ │ ├── ansi.py
│ │ │ │ │ ├── bar.py
│ │ │ │ │ ├── box.py
│ │ │ │ │ ├── cells.py
│ │ │ │ │ ├── color_triplet.py
│ │ │ │ │ ├── color.py
│ │ │ │ │ ├── columns.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── constrain.py
│ │ │ │ │ ├── containers.py
│ │ │ │ │ ├── control.py
│ │ │ │ │ ├── default_styles.py
│ │ │ │ │ ├── diagnose.py
│ │ │ │ │ ├── emoji.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_proxy.py
│ │ │ │ │ ├── filesize.py
│ │ │ │ │ ├── highlighter.py
│ │ │ │ │ ├── json.py
│ │ │ │ │ ├── jupyter.py
│ │ │ │ │ ├── layout.py
│ │ │ │ │ ├── live_render.py
│ │ │ │ │ ├── live.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── markup.py
│ │ │ │ │ ├── measure.py
│ │ │ │ │ ├── padding.py
│ │ │ │ │ ├── pager.py
│ │ │ │ │ ├── palette.py
│ │ │ │ │ ├── panel.py
│ │ │ │ │ ├── pretty.py
│ │ │ │ │ ├── progress_bar.py
│ │ │ │ │ ├── progress.py
│ │ │ │ │ ├── prompt.py
│ │ │ │ │ ├── protocol.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── region.py
│ │ │ │ │ ├── repr.py
│ │ │ │ │ ├── rule.py
│ │ │ │ │ ├── scope.py
│ │ │ │ │ ├── screen.py
│ │ │ │ │ ├── segment.py
│ │ │ │ │ ├── spinner.py
│ │ │ │ │ ├── status.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styled.py
│ │ │ │ │ ├── syntax.py
│ │ │ │ │ ├── table.py
│ │ │ │ │ ├── terminal_theme.py
│ │ │ │ │ ├── text.py
│ │ │ │ │ ├── theme.py
│ │ │ │ │ ├── themes.py
│ │ │ │ │ ├── traceback.py
│ │ │ │ │ └── tree.py
│ │ │ │ ├── tomli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _re.cpython-312.pyc
│ │ │ │ │ │ └── _types.cpython-312.pyc
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _re.py
│ │ │ │ │ ├── _types.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── truststore
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _api.cpython-312.pyc
│ │ │ │ │ │ ├── _macos.cpython-312.pyc
│ │ │ │ │ │ ├── _openssl.cpython-312.pyc
│ │ │ │ │ │ ├── _ssl_constants.cpython-312.pyc
│ │ │ │ │ │ └── _windows.cpython-312.pyc
│ │ │ │ │ ├── _api.py
│ │ │ │ │ ├── _macos.py
│ │ │ │ │ ├── _openssl.py
│ │ │ │ │ ├── _ssl_constants.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── typing_extensions.py
│ │ │ │ ├── urllib3
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── _collections.py
│ │ │ │ │ ├── _version.py
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── connectionpool.py
│ │ │ │ │ ├── contrib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _appengine_environ.cpython-312.pyc
│ │ │ │ │ │ │ ├── appengine.cpython-312.pyc
│ │ │ │ │ │ │ ├── ntlmpool.cpython-312.pyc
│ │ │ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ │ │ ├── securetransport.cpython-312.pyc
│ │ │ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ │ │ ├── _appengine_environ.py
│ │ │ │ │ │ ├── _securetransport
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── bindings.cpython-312.pyc
│ │ │ │ │ │ │ │ └── low_level.cpython-312.pyc
│ │ │ │ │ │ │ ├── bindings.py
│ │ │ │ │ │ │ └── low_level.py
│ │ │ │ │ │ ├── appengine.py
│ │ │ │ │ │ ├── ntlmpool.py
│ │ │ │ │ │ ├── pyopenssl.py
│ │ │ │ │ │ ├── securetransport.py
│ │ │ │ │ │ └── socks.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── fields.py
│ │ │ │ │ ├── filepost.py
│ │ │ │ │ ├── packages
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── six.cpython-312.pyc
│ │ │ │ │ │ ├── backports
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── makefile.cpython-312.pyc
│ │ │ │ │ │ │ │ └── weakref_finalize.cpython-312.pyc
│ │ │ │ │ │ │ ├── makefile.py
│ │ │ │ │ │ │ └── weakref_finalize.py
│ │ │ │ │ │ └── six.py
│ │ │ │ │ ├── poolmanager.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ └── util
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ │ │ ├── queue.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ │ │ └── wait.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── proxy.py
│ │ │ │ │ ├── queue.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── ssl_.py
│ │ │ │ │ ├── ssl_match_hostname.py
│ │ │ │ │ ├── ssltransport.py
│ │ │ │ │ ├── timeout.py
│ │ │ │ │ ├── url.py
│ │ │ │ │ └── wait.py
│ │ │ │ └── vendor.txt
│ │ │ └── py.typed
│ │ ├── pip-24.2.dist-info
│ │ │ ├── AUTHORS.txt
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── requests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── __version__.py
│ │ │ ├── _internal_utils.py
│ │ │ ├── adapters.py
│ │ │ ├── api.py
│ │ │ ├── auth.py
│ │ │ ├── certs.py
│ │ │ ├── compat.py
│ │ │ ├── cookies.py
│ │ │ ├── exceptions.py
│ │ │ ├── help.py
│ │ │ ├── hooks.py
│ │ │ ├── models.py
│ │ │ ├── packages.py
│ │ │ ├── sessions.py
│ │ │ ├── status_codes.py
│ │ │ ├── structures.py
│ │ │ └── utils.py
│ │ ├── requests-2.32.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── soupsieve
│ │ │ ├── __init__.py
│ │ │ ├── __meta__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __meta__.cpython-312.pyc
│ │ │ │ ├── css_match.cpython-312.pyc
│ │ │ │ ├── css_parser.cpython-312.pyc
│ │ │ │ ├── css_types.cpython-312.pyc
│ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ └── util.cpython-312.pyc
│ │ │ ├── css_match.py
│ │ │ ├── css_parser.py
│ │ │ ├── css_types.py
│ │ │ ├── pretty.py
│ │ │ ├── py.typed
│ │ │ └── util.py
│ │ ├── soupsieve-2.6.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── urllib3
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _base_connection.cpython-312.pyc
│ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ ├── _request_methods.cpython-312.pyc
│ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ └── response.cpython-312.pyc
│ │ │ ├── _base_connection.py
│ │ │ ├── _collections.py
│ │ │ ├── _request_methods.py
│ │ │ ├── _version.py
│ │ │ ├── connection.py
│ │ │ ├── connectionpool.py
│ │ │ ├── contrib
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ ├── emscripten
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── fetch.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── emscripten_fetch_worker.js
│ │ │ │ │ ├── fetch.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ └── response.py
│ │ │ │ ├── pyopenssl.py
│ │ │ │ └── socks.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── filepost.py
│ │ │ ├── http2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ └── probe.cpython-312.pyc
│ │ │ │ ├── connection.py
│ │ │ │ └── probe.py
│ │ │ ├── poolmanager.py
│ │ │ ├── py.typed
│ │ │ ├── response.py
│ │ │ └── util
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ └── wait.cpython-312.pyc
│ │ │ ├── connection.py
│ │ │ ├── proxy.py
│ │ │ ├── request.py
│ │ │ ├── response.py
│ │ │ ├── retry.py
│ │ │ ├── ssl_.py
│ │ │ ├── ssl_match_hostname.py
│ │ │ ├── ssltransport.py
│ │ │ ├── timeout.py
│ │ │ ├── url.py
│ │ │ ├── util.py
│ │ │ └── wait.py
│ │ ├── urllib3-2.2.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── useragent
│ │ │ ├── __init__.py
│ │ │ ├── __init__.pyc
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── resources
│ │ │ │ └── user_agent_data.json
│ │ │ └── test
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── test_additional_os.json
│ │ │ ├── test_browser.json
│ │ │ ├── test_device.json
│ │ │ ├── test_firefox.json
│ │ │ ├── test_os.json
│ │ │ └── test_pgts_browser.json
│ │ ├── useragent-0.1.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── werkzeug
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _internal.cpython-312.pyc
│ │ │ │ ├── _reloader.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formparser.cpython-312.pyc
│ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ ├── local.cpython-312.pyc
│ │ │ │ ├── security.cpython-312.pyc
│ │ │ │ ├── serving.cpython-312.pyc
│ │ │ │ ├── test.cpython-312.pyc
│ │ │ │ ├── testapp.cpython-312.pyc
│ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ ├── user_agent.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── wsgi.cpython-312.pyc
│ │ │ ├── _internal.py
│ │ │ ├── _reloader.py
│ │ │ ├── datastructures
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── accept.cpython-312.pyc
│ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ ├── cache_control.cpython-312.pyc
│ │ │ │ │ ├── csp.cpython-312.pyc
│ │ │ │ │ ├── etag.cpython-312.pyc
│ │ │ │ │ ├── file_storage.cpython-312.pyc
│ │ │ │ │ ├── headers.cpython-312.pyc
│ │ │ │ │ ├── mixins.cpython-312.pyc
│ │ │ │ │ ├── range.cpython-312.pyc
│ │ │ │ │ └── structures.cpython-312.pyc
│ │ │ │ ├── accept.py
│ │ │ │ ├── accept.pyi
│ │ │ │ ├── auth.py
│ │ │ │ ├── cache_control.py
│ │ │ │ ├── cache_control.pyi
│ │ │ │ ├── csp.py
│ │ │ │ ├── csp.pyi
│ │ │ │ ├── etag.py
│ │ │ │ ├── etag.pyi
│ │ │ │ ├── file_storage.py
│ │ │ │ ├── file_storage.pyi
│ │ │ │ ├── headers.py
│ │ │ │ ├── headers.pyi
│ │ │ │ ├── mixins.py
│ │ │ │ ├── mixins.pyi
│ │ │ │ ├── range.py
│ │ │ │ ├── range.pyi
│ │ │ │ ├── structures.py
│ │ │ │ └── structures.pyi
│ │ │ ├── debug
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ └── tbtools.cpython-312.pyc
│ │ │ │ ├── console.py
│ │ │ │ ├── repr.py
│ │ │ │ ├── shared
│ │ │ │ │ ├── console.png
│ │ │ │ │ ├── debugger.js
│ │ │ │ │ ├── ICON_LICENSE.md
│ │ │ │ │ ├── less.png
│ │ │ │ │ ├── more.png
│ │ │ │ │ └── style.css
│ │ │ │ └── tbtools.py
│ │ │ ├── exceptions.py
│ │ │ ├── formparser.py
│ │ │ ├── http.py
│ │ │ ├── local.py
│ │ │ ├── middleware
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── dispatcher.cpython-312.pyc
│ │ │ │ │ ├── http_proxy.cpython-312.pyc
│ │ │ │ │ ├── lint.cpython-312.pyc
│ │ │ │ │ ├── profiler.cpython-312.pyc
│ │ │ │ │ ├── proxy_fix.cpython-312.pyc
│ │ │ │ │ └── shared_data.cpython-312.pyc
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── http_proxy.py
│ │ │ │ ├── lint.py
│ │ │ │ ├── profiler.py
│ │ │ │ ├── proxy_fix.py
│ │ │ │ └── shared_data.py
│ │ │ ├── py.typed
│ │ │ ├── routing
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── converters.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── map.cpython-312.pyc
│ │ │ │ │ ├── matcher.cpython-312.pyc
│ │ │ │ │ └── rules.cpython-312.pyc
│ │ │ │ ├── converters.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── map.py
│ │ │ │ ├── matcher.py
│ │ │ │ └── rules.py
│ │ │ ├── sansio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ │ ├── multipart.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ ├── http.py
│ │ │ │ ├── multipart.py
│ │ │ │ ├── request.py
│ │ │ │ ├── response.py
│ │ │ │ └── utils.py
│ │ │ ├── security.py
│ │ │ ├── serving.py
│ │ │ ├── test.py
│ │ │ ├── testapp.py
│ │ │ ├── urls.py
│ │ │ ├── user_agent.py
│ │ │ ├── utils.py
│ │ │ ├── wrappers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ ├── request.py
│ │ │ │ └── response.py
│ │ │ └── wsgi.py
│ │ └── werkzeug-3.0.4.dist-info
│ │ ├── INSTALLER
│ │ ├── LICENSE.txt
│ │ ├── METADATA
│ │ ├── RECORD
│ │ └── WHEEL
│ ├── pyvenv.cfg
│ ├── static
│ │ └── styles.css
│ ├── templates
│ │ └── index.html
│ └── test.py
├── cline_config.json
├── mcp_server.py
├── README.md
├── search_results.json
├── settings.json
└── test_files
├── text1.txt
└── text2.txt
```
# Files
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py:
--------------------------------------------------------------------------------
```python
1 | """Backing implementation for InstallRequirement's various constructors
2 |
3 | The idea here is that these formed a major chunk of InstallRequirement's size
4 | so, moving them and support code dedicated to them outside of that class
5 | helps creates for better understandability for the rest of the code.
6 |
7 | These are meant to be used elsewhere within pip to create instances of
8 | InstallRequirement.
9 | """
10 |
11 | import copy
12 | import logging
13 | import os
14 | import re
15 | from dataclasses import dataclass
16 | from typing import Collection, Dict, List, Optional, Set, Tuple, Union
17 |
18 | from pip._vendor.packaging.markers import Marker
19 | from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
20 | from pip._vendor.packaging.specifiers import Specifier
21 |
22 | from pip._internal.exceptions import InstallationError
23 | from pip._internal.models.index import PyPI, TestPyPI
24 | from pip._internal.models.link import Link
25 | from pip._internal.models.wheel import Wheel
26 | from pip._internal.req.req_file import ParsedRequirement
27 | from pip._internal.req.req_install import InstallRequirement
28 | from pip._internal.utils.filetypes import is_archive_file
29 | from pip._internal.utils.misc import is_installable_dir
30 | from pip._internal.utils.packaging import get_requirement
31 | from pip._internal.utils.urls import path_to_url
32 | from pip._internal.vcs import is_url, vcs
33 |
34 | __all__ = [
35 | "install_req_from_editable",
36 | "install_req_from_line",
37 | "parse_editable",
38 | ]
39 |
40 | logger = logging.getLogger(__name__)
41 | operators = Specifier._operators.keys()
42 |
43 |
44 | def _strip_extras(path: str) -> Tuple[str, Optional[str]]:
45 | m = re.match(r"^(.+)(\[[^\]]+\])$", path)
46 | extras = None
47 | if m:
48 | path_no_extras = m.group(1)
49 | extras = m.group(2)
50 | else:
51 | path_no_extras = path
52 |
53 | return path_no_extras, extras
54 |
55 |
56 | def convert_extras(extras: Optional[str]) -> Set[str]:
57 | if not extras:
58 | return set()
59 | return get_requirement("placeholder" + extras.lower()).extras
60 |
61 |
62 | def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requirement:
63 | """
64 | Returns a new requirement based on the given one, with the supplied extras. If the
65 | given requirement already has extras those are replaced (or dropped if no new extras
66 | are given).
67 | """
68 | match: Optional[re.Match[str]] = re.fullmatch(
69 | # see https://peps.python.org/pep-0508/#complete-grammar
70 | r"([\w\t .-]+)(\[[^\]]*\])?(.*)",
71 | str(req),
72 | flags=re.ASCII,
73 | )
74 | # ireq.req is a valid requirement so the regex should always match
75 | assert (
76 | match is not None
77 | ), f"regex match on requirement {req} failed, this should never happen"
78 | pre: Optional[str] = match.group(1)
79 | post: Optional[str] = match.group(3)
80 | assert (
81 | pre is not None and post is not None
82 | ), f"regex group selection for requirement {req} failed, this should never happen"
83 | extras: str = "[%s]" % ",".join(sorted(new_extras)) if new_extras else ""
84 | return get_requirement(f"{pre}{extras}{post}")
85 |
86 |
87 | def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
88 | """Parses an editable requirement into:
89 | - a requirement name
90 | - an URL
91 | - extras
92 | - editable options
93 | Accepted requirements:
94 | svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
95 | .[some_extra]
96 | """
97 |
98 | url = editable_req
99 |
100 | # If a file path is specified with extras, strip off the extras.
101 | url_no_extras, extras = _strip_extras(url)
102 |
103 | if os.path.isdir(url_no_extras):
104 | # Treating it as code that has already been checked out
105 | url_no_extras = path_to_url(url_no_extras)
106 |
107 | if url_no_extras.lower().startswith("file:"):
108 | package_name = Link(url_no_extras).egg_fragment
109 | if extras:
110 | return (
111 | package_name,
112 | url_no_extras,
113 | get_requirement("placeholder" + extras.lower()).extras,
114 | )
115 | else:
116 | return package_name, url_no_extras, set()
117 |
118 | for version_control in vcs:
119 | if url.lower().startswith(f"{version_control}:"):
120 | url = f"{version_control}+{url}"
121 | break
122 |
123 | link = Link(url)
124 |
125 | if not link.is_vcs:
126 | backends = ", ".join(vcs.all_schemes)
127 | raise InstallationError(
128 | f"{editable_req} is not a valid editable requirement. "
129 | f"It should either be a path to a local project or a VCS URL "
130 | f"(beginning with {backends})."
131 | )
132 |
133 | package_name = link.egg_fragment
134 | if not package_name:
135 | raise InstallationError(
136 | f"Could not detect requirement name for '{editable_req}', "
137 | "please specify one with #egg=your_package_name"
138 | )
139 | return package_name, url, set()
140 |
141 |
142 | def check_first_requirement_in_file(filename: str) -> None:
143 | """Check if file is parsable as a requirements file.
144 |
145 | This is heavily based on ``pkg_resources.parse_requirements``, but
146 | simplified to just check the first meaningful line.
147 |
148 | :raises InvalidRequirement: If the first meaningful line cannot be parsed
149 | as an requirement.
150 | """
151 | with open(filename, encoding="utf-8", errors="ignore") as f:
152 | # Create a steppable iterator, so we can handle \-continuations.
153 | lines = (
154 | line
155 | for line in (line.strip() for line in f)
156 | if line and not line.startswith("#") # Skip blank lines/comments.
157 | )
158 |
159 | for line in lines:
160 | # Drop comments -- a hash without a space may be in a URL.
161 | if " #" in line:
162 | line = line[: line.find(" #")]
163 | # If there is a line continuation, drop it, and append the next line.
164 | if line.endswith("\\"):
165 | line = line[:-2].strip() + next(lines, "")
166 | get_requirement(line)
167 | return
168 |
169 |
170 | def deduce_helpful_msg(req: str) -> str:
171 | """Returns helpful msg in case requirements file does not exist,
172 | or cannot be parsed.
173 |
174 | :params req: Requirements file path
175 | """
176 | if not os.path.exists(req):
177 | return f" File '{req}' does not exist."
178 | msg = " The path does exist. "
179 | # Try to parse and check if it is a requirements file.
180 | try:
181 | check_first_requirement_in_file(req)
182 | except InvalidRequirement:
183 | logger.debug("Cannot parse '%s' as requirements file", req)
184 | else:
185 | msg += (
186 | f"The argument you provided "
187 | f"({req}) appears to be a"
188 | f" requirements file. If that is the"
189 | f" case, use the '-r' flag to install"
190 | f" the packages specified within it."
191 | )
192 | return msg
193 |
194 |
195 | @dataclass(frozen=True)
196 | class RequirementParts:
197 | requirement: Optional[Requirement]
198 | link: Optional[Link]
199 | markers: Optional[Marker]
200 | extras: Set[str]
201 |
202 |
203 | def parse_req_from_editable(editable_req: str) -> RequirementParts:
204 | name, url, extras_override = parse_editable(editable_req)
205 |
206 | if name is not None:
207 | try:
208 | req: Optional[Requirement] = get_requirement(name)
209 | except InvalidRequirement as exc:
210 | raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
211 | else:
212 | req = None
213 |
214 | link = Link(url)
215 |
216 | return RequirementParts(req, link, None, extras_override)
217 |
218 |
219 | # ---- The actual constructors follow ----
220 |
221 |
222 | def install_req_from_editable(
223 | editable_req: str,
224 | comes_from: Optional[Union[InstallRequirement, str]] = None,
225 | *,
226 | use_pep517: Optional[bool] = None,
227 | isolated: bool = False,
228 | global_options: Optional[List[str]] = None,
229 | hash_options: Optional[Dict[str, List[str]]] = None,
230 | constraint: bool = False,
231 | user_supplied: bool = False,
232 | permit_editable_wheels: bool = False,
233 | config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
234 | ) -> InstallRequirement:
235 | parts = parse_req_from_editable(editable_req)
236 |
237 | return InstallRequirement(
238 | parts.requirement,
239 | comes_from=comes_from,
240 | user_supplied=user_supplied,
241 | editable=True,
242 | permit_editable_wheels=permit_editable_wheels,
243 | link=parts.link,
244 | constraint=constraint,
245 | use_pep517=use_pep517,
246 | isolated=isolated,
247 | global_options=global_options,
248 | hash_options=hash_options,
249 | config_settings=config_settings,
250 | extras=parts.extras,
251 | )
252 |
253 |
254 | def _looks_like_path(name: str) -> bool:
255 | """Checks whether the string "looks like" a path on the filesystem.
256 |
257 | This does not check whether the target actually exists, only judge from the
258 | appearance.
259 |
260 | Returns true if any of the following conditions is true:
261 | * a path separator is found (either os.path.sep or os.path.altsep);
262 | * a dot is found (which represents the current directory).
263 | """
264 | if os.path.sep in name:
265 | return True
266 | if os.path.altsep is not None and os.path.altsep in name:
267 | return True
268 | if name.startswith("."):
269 | return True
270 | return False
271 |
272 |
273 | def _get_url_from_path(path: str, name: str) -> Optional[str]:
274 | """
275 | First, it checks whether a provided path is an installable directory. If it
276 | is, returns the path.
277 |
278 | If false, check if the path is an archive file (such as a .whl).
279 | The function checks if the path is a file. If false, if the path has
280 | an @, it will treat it as a PEP 440 URL requirement and return the path.
281 | """
282 | if _looks_like_path(name) and os.path.isdir(path):
283 | if is_installable_dir(path):
284 | return path_to_url(path)
285 | # TODO: The is_installable_dir test here might not be necessary
286 | # now that it is done in load_pyproject_toml too.
287 | raise InstallationError(
288 | f"Directory {name!r} is not installable. Neither 'setup.py' "
289 | "nor 'pyproject.toml' found."
290 | )
291 | if not is_archive_file(path):
292 | return None
293 | if os.path.isfile(path):
294 | return path_to_url(path)
295 | urlreq_parts = name.split("@", 1)
296 | if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]):
297 | # If the path contains '@' and the part before it does not look
298 | # like a path, try to treat it as a PEP 440 URL req instead.
299 | return None
300 | logger.warning(
301 | "Requirement %r looks like a filename, but the file does not exist",
302 | name,
303 | )
304 | return path_to_url(path)
305 |
306 |
307 | def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts:
308 | if is_url(name):
309 | marker_sep = "; "
310 | else:
311 | marker_sep = ";"
312 | if marker_sep in name:
313 | name, markers_as_string = name.split(marker_sep, 1)
314 | markers_as_string = markers_as_string.strip()
315 | if not markers_as_string:
316 | markers = None
317 | else:
318 | markers = Marker(markers_as_string)
319 | else:
320 | markers = None
321 | name = name.strip()
322 | req_as_string = None
323 | path = os.path.normpath(os.path.abspath(name))
324 | link = None
325 | extras_as_string = None
326 |
327 | if is_url(name):
328 | link = Link(name)
329 | else:
330 | p, extras_as_string = _strip_extras(path)
331 | url = _get_url_from_path(p, name)
332 | if url is not None:
333 | link = Link(url)
334 |
335 | # it's a local file, dir, or url
336 | if link:
337 | # Handle relative file URLs
338 | if link.scheme == "file" and re.search(r"\.\./", link.url):
339 | link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path))))
340 | # wheel file
341 | if link.is_wheel:
342 | wheel = Wheel(link.filename) # can raise InvalidWheelFilename
343 | req_as_string = f"{wheel.name}=={wheel.version}"
344 | else:
345 | # set the req to the egg fragment. when it's not there, this
346 | # will become an 'unnamed' requirement
347 | req_as_string = link.egg_fragment
348 |
349 | # a requirement specifier
350 | else:
351 | req_as_string = name
352 |
353 | extras = convert_extras(extras_as_string)
354 |
355 | def with_source(text: str) -> str:
356 | if not line_source:
357 | return text
358 | return f"{text} (from {line_source})"
359 |
360 | def _parse_req_string(req_as_string: str) -> Requirement:
361 | try:
362 | return get_requirement(req_as_string)
363 | except InvalidRequirement as exc:
364 | if os.path.sep in req_as_string:
365 | add_msg = "It looks like a path."
366 | add_msg += deduce_helpful_msg(req_as_string)
367 | elif "=" in req_as_string and not any(
368 | op in req_as_string for op in operators
369 | ):
370 | add_msg = "= is not a valid operator. Did you mean == ?"
371 | else:
372 | add_msg = ""
373 | msg = with_source(f"Invalid requirement: {req_as_string!r}: {exc}")
374 | if add_msg:
375 | msg += f"\nHint: {add_msg}"
376 | raise InstallationError(msg)
377 |
378 | if req_as_string is not None:
379 | req: Optional[Requirement] = _parse_req_string(req_as_string)
380 | else:
381 | req = None
382 |
383 | return RequirementParts(req, link, markers, extras)
384 |
385 |
386 | def install_req_from_line(
387 | name: str,
388 | comes_from: Optional[Union[str, InstallRequirement]] = None,
389 | *,
390 | use_pep517: Optional[bool] = None,
391 | isolated: bool = False,
392 | global_options: Optional[List[str]] = None,
393 | hash_options: Optional[Dict[str, List[str]]] = None,
394 | constraint: bool = False,
395 | line_source: Optional[str] = None,
396 | user_supplied: bool = False,
397 | config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
398 | ) -> InstallRequirement:
399 | """Creates an InstallRequirement from a name, which might be a
400 | requirement, directory containing 'setup.py', filename, or URL.
401 |
402 | :param line_source: An optional string describing where the line is from,
403 | for logging purposes in case of an error.
404 | """
405 | parts = parse_req_from_line(name, line_source)
406 |
407 | return InstallRequirement(
408 | parts.requirement,
409 | comes_from,
410 | link=parts.link,
411 | markers=parts.markers,
412 | use_pep517=use_pep517,
413 | isolated=isolated,
414 | global_options=global_options,
415 | hash_options=hash_options,
416 | config_settings=config_settings,
417 | constraint=constraint,
418 | extras=parts.extras,
419 | user_supplied=user_supplied,
420 | )
421 |
422 |
423 | def install_req_from_req_string(
424 | req_string: str,
425 | comes_from: Optional[InstallRequirement] = None,
426 | isolated: bool = False,
427 | use_pep517: Optional[bool] = None,
428 | user_supplied: bool = False,
429 | ) -> InstallRequirement:
430 | try:
431 | req = get_requirement(req_string)
432 | except InvalidRequirement as exc:
433 | raise InstallationError(f"Invalid requirement: {req_string!r}: {exc}")
434 |
435 | domains_not_allowed = [
436 | PyPI.file_storage_domain,
437 | TestPyPI.file_storage_domain,
438 | ]
439 | if (
440 | req.url
441 | and comes_from
442 | and comes_from.link
443 | and comes_from.link.netloc in domains_not_allowed
444 | ):
445 | # Explicitly disallow pypi packages that depend on external urls
446 | raise InstallationError(
447 | "Packages installed from PyPI cannot depend on packages "
448 | "which are not also hosted on PyPI.\n"
449 | f"{comes_from.name} depends on {req} "
450 | )
451 |
452 | return InstallRequirement(
453 | req,
454 | comes_from,
455 | isolated=isolated,
456 | use_pep517=use_pep517,
457 | user_supplied=user_supplied,
458 | )
459 |
460 |
461 | def install_req_from_parsed_requirement(
462 | parsed_req: ParsedRequirement,
463 | isolated: bool = False,
464 | use_pep517: Optional[bool] = None,
465 | user_supplied: bool = False,
466 | config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
467 | ) -> InstallRequirement:
468 | if parsed_req.is_editable:
469 | req = install_req_from_editable(
470 | parsed_req.requirement,
471 | comes_from=parsed_req.comes_from,
472 | use_pep517=use_pep517,
473 | constraint=parsed_req.constraint,
474 | isolated=isolated,
475 | user_supplied=user_supplied,
476 | config_settings=config_settings,
477 | )
478 |
479 | else:
480 | req = install_req_from_line(
481 | parsed_req.requirement,
482 | comes_from=parsed_req.comes_from,
483 | use_pep517=use_pep517,
484 | isolated=isolated,
485 | global_options=(
486 | parsed_req.options.get("global_options", [])
487 | if parsed_req.options
488 | else []
489 | ),
490 | hash_options=(
491 | parsed_req.options.get("hashes", {}) if parsed_req.options else {}
492 | ),
493 | constraint=parsed_req.constraint,
494 | line_source=parsed_req.line_source,
495 | user_supplied=user_supplied,
496 | config_settings=config_settings,
497 | )
498 | return req
499 |
500 |
501 | def install_req_from_link_and_ireq(
502 | link: Link, ireq: InstallRequirement
503 | ) -> InstallRequirement:
504 | return InstallRequirement(
505 | req=ireq.req,
506 | comes_from=ireq.comes_from,
507 | editable=ireq.editable,
508 | link=link,
509 | markers=ireq.markers,
510 | use_pep517=ireq.use_pep517,
511 | isolated=ireq.isolated,
512 | global_options=ireq.global_options,
513 | hash_options=ireq.hash_options,
514 | config_settings=ireq.config_settings,
515 | user_supplied=ireq.user_supplied,
516 | )
517 |
518 |
519 | def install_req_drop_extras(ireq: InstallRequirement) -> InstallRequirement:
520 | """
521 | Creates a new InstallationRequirement using the given template but without
522 | any extras. Sets the original requirement as the new one's parent
523 | (comes_from).
524 | """
525 | return InstallRequirement(
526 | req=(
527 | _set_requirement_extras(ireq.req, set()) if ireq.req is not None else None
528 | ),
529 | comes_from=ireq,
530 | editable=ireq.editable,
531 | link=ireq.link,
532 | markers=ireq.markers,
533 | use_pep517=ireq.use_pep517,
534 | isolated=ireq.isolated,
535 | global_options=ireq.global_options,
536 | hash_options=ireq.hash_options,
537 | constraint=ireq.constraint,
538 | extras=[],
539 | config_settings=ireq.config_settings,
540 | user_supplied=ireq.user_supplied,
541 | permit_editable_wheels=ireq.permit_editable_wheels,
542 | )
543 |
544 |
545 | def install_req_extend_extras(
546 | ireq: InstallRequirement,
547 | extras: Collection[str],
548 | ) -> InstallRequirement:
549 | """
550 | Returns a copy of an installation requirement with some additional extras.
551 | Makes a shallow copy of the ireq object.
552 | """
553 | result = copy.copy(ireq)
554 | result.extras = {*ireq.extras, *extras}
555 | result.req = (
556 | _set_requirement_extras(ireq.req, result.extras)
557 | if ireq.req is not None
558 | else None
559 | )
560 | return result
561 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/urllib3/util/retry.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import email
4 | import logging
5 | import random
6 | import re
7 | import time
8 | import typing
9 | from itertools import takewhile
10 | from types import TracebackType
11 |
12 | from ..exceptions import (
13 | ConnectTimeoutError,
14 | InvalidHeader,
15 | MaxRetryError,
16 | ProtocolError,
17 | ProxyError,
18 | ReadTimeoutError,
19 | ResponseError,
20 | )
21 | from .util import reraise
22 |
23 | if typing.TYPE_CHECKING:
24 | from typing_extensions import Self
25 |
26 | from ..connectionpool import ConnectionPool
27 | from ..response import BaseHTTPResponse
28 |
29 | log = logging.getLogger(__name__)
30 |
31 |
32 | # Data structure for representing the metadata of requests that result in a retry.
33 | class RequestHistory(typing.NamedTuple):
34 | method: str | None
35 | url: str | None
36 | error: Exception | None
37 | status: int | None
38 | redirect_location: str | None
39 |
40 |
41 | class Retry:
42 | """Retry configuration.
43 |
44 | Each retry attempt will create a new Retry object with updated values, so
45 | they can be safely reused.
46 |
47 | Retries can be defined as a default for a pool:
48 |
49 | .. code-block:: python
50 |
51 | retries = Retry(connect=5, read=2, redirect=5)
52 | http = PoolManager(retries=retries)
53 | response = http.request("GET", "https://example.com/")
54 |
55 | Or per-request (which overrides the default for the pool):
56 |
57 | .. code-block:: python
58 |
59 | response = http.request("GET", "https://example.com/", retries=Retry(10))
60 |
61 | Retries can be disabled by passing ``False``:
62 |
63 | .. code-block:: python
64 |
65 | response = http.request("GET", "https://example.com/", retries=False)
66 |
67 | Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless
68 | retries are disabled, in which case the causing exception will be raised.
69 |
70 | :param int total:
71 | Total number of retries to allow. Takes precedence over other counts.
72 |
73 | Set to ``None`` to remove this constraint and fall back on other
74 | counts.
75 |
76 | Set to ``0`` to fail on the first retry.
77 |
78 | Set to ``False`` to disable and imply ``raise_on_redirect=False``.
79 |
80 | :param int connect:
81 | How many connection-related errors to retry on.
82 |
83 | These are errors raised before the request is sent to the remote server,
84 | which we assume has not triggered the server to process the request.
85 |
86 | Set to ``0`` to fail on the first retry of this type.
87 |
88 | :param int read:
89 | How many times to retry on read errors.
90 |
91 | These errors are raised after the request was sent to the server, so the
92 | request may have side-effects.
93 |
94 | Set to ``0`` to fail on the first retry of this type.
95 |
96 | :param int redirect:
97 | How many redirects to perform. Limit this to avoid infinite redirect
98 | loops.
99 |
100 | A redirect is a HTTP response with a status code 301, 302, 303, 307 or
101 | 308.
102 |
103 | Set to ``0`` to fail on the first retry of this type.
104 |
105 | Set to ``False`` to disable and imply ``raise_on_redirect=False``.
106 |
107 | :param int status:
108 | How many times to retry on bad status codes.
109 |
110 | These are retries made on responses, where status code matches
111 | ``status_forcelist``.
112 |
113 | Set to ``0`` to fail on the first retry of this type.
114 |
115 | :param int other:
116 | How many times to retry on other errors.
117 |
118 | Other errors are errors that are not connect, read, redirect or status errors.
119 | These errors might be raised after the request was sent to the server, so the
120 | request might have side-effects.
121 |
122 | Set to ``0`` to fail on the first retry of this type.
123 |
124 | If ``total`` is not set, it's a good idea to set this to 0 to account
125 | for unexpected edge cases and avoid infinite retry loops.
126 |
127 | :param Collection allowed_methods:
128 | Set of uppercased HTTP method verbs that we should retry on.
129 |
130 | By default, we only retry on methods which are considered to be
131 | idempotent (multiple requests with the same parameters end with the
132 | same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`.
133 |
134 | Set to a ``None`` value to retry on any verb.
135 |
136 | :param Collection status_forcelist:
137 | A set of integer HTTP status codes that we should force a retry on.
138 | A retry is initiated if the request method is in ``allowed_methods``
139 | and the response status code is in ``status_forcelist``.
140 |
141 | By default, this is disabled with ``None``.
142 |
143 | :param float backoff_factor:
144 | A backoff factor to apply between attempts after the second try
145 | (most errors are resolved immediately by a second try without a
146 | delay). urllib3 will sleep for::
147 |
148 | {backoff factor} * (2 ** ({number of previous retries}))
149 |
150 | seconds. If `backoff_jitter` is non-zero, this sleep is extended by::
151 |
152 | random.uniform(0, {backoff jitter})
153 |
154 | seconds. For example, if the backoff_factor is 0.1, then :func:`Retry.sleep` will
155 | sleep for [0.0s, 0.2s, 0.4s, 0.8s, ...] between retries. No backoff will ever
156 | be longer than `backoff_max`.
157 |
158 | By default, backoff is disabled (factor set to 0).
159 |
160 | :param bool raise_on_redirect: Whether, if the number of redirects is
161 | exhausted, to raise a MaxRetryError, or to return a response with a
162 | response code in the 3xx range.
163 |
164 | :param bool raise_on_status: Similar meaning to ``raise_on_redirect``:
165 | whether we should raise an exception, or return a response,
166 | if status falls in ``status_forcelist`` range and retries have
167 | been exhausted.
168 |
169 | :param tuple history: The history of the request encountered during
170 | each call to :meth:`~Retry.increment`. The list is in the order
171 | the requests occurred. Each list item is of class :class:`RequestHistory`.
172 |
173 | :param bool respect_retry_after_header:
174 | Whether to respect Retry-After header on status codes defined as
175 | :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not.
176 |
177 | :param Collection remove_headers_on_redirect:
178 | Sequence of headers to remove from the request when a response
179 | indicating a redirect is returned before firing off the redirected
180 | request.
181 | """
182 |
183 | #: Default methods to be used for ``allowed_methods``
184 | DEFAULT_ALLOWED_METHODS = frozenset(
185 | ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"]
186 | )
187 |
188 | #: Default status codes to be used for ``status_forcelist``
189 | RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503])
190 |
191 | #: Default headers to be used for ``remove_headers_on_redirect``
192 | DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(
193 | ["Cookie", "Authorization", "Proxy-Authorization"]
194 | )
195 |
196 | #: Default maximum backoff time.
197 | DEFAULT_BACKOFF_MAX = 120
198 |
199 | # Backward compatibility; assigned outside of the class.
200 | DEFAULT: typing.ClassVar[Retry]
201 |
202 | def __init__(
203 | self,
204 | total: bool | int | None = 10,
205 | connect: int | None = None,
206 | read: int | None = None,
207 | redirect: bool | int | None = None,
208 | status: int | None = None,
209 | other: int | None = None,
210 | allowed_methods: typing.Collection[str] | None = DEFAULT_ALLOWED_METHODS,
211 | status_forcelist: typing.Collection[int] | None = None,
212 | backoff_factor: float = 0,
213 | backoff_max: float = DEFAULT_BACKOFF_MAX,
214 | raise_on_redirect: bool = True,
215 | raise_on_status: bool = True,
216 | history: tuple[RequestHistory, ...] | None = None,
217 | respect_retry_after_header: bool = True,
218 | remove_headers_on_redirect: typing.Collection[
219 | str
220 | ] = DEFAULT_REMOVE_HEADERS_ON_REDIRECT,
221 | backoff_jitter: float = 0.0,
222 | ) -> None:
223 | self.total = total
224 | self.connect = connect
225 | self.read = read
226 | self.status = status
227 | self.other = other
228 |
229 | if redirect is False or total is False:
230 | redirect = 0
231 | raise_on_redirect = False
232 |
233 | self.redirect = redirect
234 | self.status_forcelist = status_forcelist or set()
235 | self.allowed_methods = allowed_methods
236 | self.backoff_factor = backoff_factor
237 | self.backoff_max = backoff_max
238 | self.raise_on_redirect = raise_on_redirect
239 | self.raise_on_status = raise_on_status
240 | self.history = history or ()
241 | self.respect_retry_after_header = respect_retry_after_header
242 | self.remove_headers_on_redirect = frozenset(
243 | h.lower() for h in remove_headers_on_redirect
244 | )
245 | self.backoff_jitter = backoff_jitter
246 |
247 | def new(self, **kw: typing.Any) -> Self:
248 | params = dict(
249 | total=self.total,
250 | connect=self.connect,
251 | read=self.read,
252 | redirect=self.redirect,
253 | status=self.status,
254 | other=self.other,
255 | allowed_methods=self.allowed_methods,
256 | status_forcelist=self.status_forcelist,
257 | backoff_factor=self.backoff_factor,
258 | backoff_max=self.backoff_max,
259 | raise_on_redirect=self.raise_on_redirect,
260 | raise_on_status=self.raise_on_status,
261 | history=self.history,
262 | remove_headers_on_redirect=self.remove_headers_on_redirect,
263 | respect_retry_after_header=self.respect_retry_after_header,
264 | backoff_jitter=self.backoff_jitter,
265 | )
266 |
267 | params.update(kw)
268 | return type(self)(**params) # type: ignore[arg-type]
269 |
270 | @classmethod
271 | def from_int(
272 | cls,
273 | retries: Retry | bool | int | None,
274 | redirect: bool | int | None = True,
275 | default: Retry | bool | int | None = None,
276 | ) -> Retry:
277 | """Backwards-compatibility for the old retries format."""
278 | if retries is None:
279 | retries = default if default is not None else cls.DEFAULT
280 |
281 | if isinstance(retries, Retry):
282 | return retries
283 |
284 | redirect = bool(redirect) and None
285 | new_retries = cls(retries, redirect=redirect)
286 | log.debug("Converted retries value: %r -> %r", retries, new_retries)
287 | return new_retries
288 |
289 | def get_backoff_time(self) -> float:
290 | """Formula for computing the current backoff
291 |
292 | :rtype: float
293 | """
294 | # We want to consider only the last consecutive errors sequence (Ignore redirects).
295 | consecutive_errors_len = len(
296 | list(
297 | takewhile(lambda x: x.redirect_location is None, reversed(self.history))
298 | )
299 | )
300 | if consecutive_errors_len <= 1:
301 | return 0
302 |
303 | backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1))
304 | if self.backoff_jitter != 0.0:
305 | backoff_value += random.random() * self.backoff_jitter
306 | return float(max(0, min(self.backoff_max, backoff_value)))
307 |
308 | def parse_retry_after(self, retry_after: str) -> float:
309 | seconds: float
310 | # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4
311 | if re.match(r"^\s*[0-9]+\s*$", retry_after):
312 | seconds = int(retry_after)
313 | else:
314 | retry_date_tuple = email.utils.parsedate_tz(retry_after)
315 | if retry_date_tuple is None:
316 | raise InvalidHeader(f"Invalid Retry-After header: {retry_after}")
317 |
318 | retry_date = email.utils.mktime_tz(retry_date_tuple)
319 | seconds = retry_date - time.time()
320 |
321 | seconds = max(seconds, 0)
322 |
323 | return seconds
324 |
325 | def get_retry_after(self, response: BaseHTTPResponse) -> float | None:
326 | """Get the value of Retry-After in seconds."""
327 |
328 | retry_after = response.headers.get("Retry-After")
329 |
330 | if retry_after is None:
331 | return None
332 |
333 | return self.parse_retry_after(retry_after)
334 |
335 | def sleep_for_retry(self, response: BaseHTTPResponse) -> bool:
336 | retry_after = self.get_retry_after(response)
337 | if retry_after:
338 | time.sleep(retry_after)
339 | return True
340 |
341 | return False
342 |
343 | def _sleep_backoff(self) -> None:
344 | backoff = self.get_backoff_time()
345 | if backoff <= 0:
346 | return
347 | time.sleep(backoff)
348 |
349 | def sleep(self, response: BaseHTTPResponse | None = None) -> None:
350 | """Sleep between retry attempts.
351 |
352 | This method will respect a server's ``Retry-After`` response header
353 | and sleep the duration of the time requested. If that is not present, it
354 | will use an exponential backoff. By default, the backoff factor is 0 and
355 | this method will return immediately.
356 | """
357 |
358 | if self.respect_retry_after_header and response:
359 | slept = self.sleep_for_retry(response)
360 | if slept:
361 | return
362 |
363 | self._sleep_backoff()
364 |
365 | def _is_connection_error(self, err: Exception) -> bool:
366 | """Errors when we're fairly sure that the server did not receive the
367 | request, so it should be safe to retry.
368 | """
369 | if isinstance(err, ProxyError):
370 | err = err.original_error
371 | return isinstance(err, ConnectTimeoutError)
372 |
373 | def _is_read_error(self, err: Exception) -> bool:
374 | """Errors that occur after the request has been started, so we should
375 | assume that the server began processing it.
376 | """
377 | return isinstance(err, (ReadTimeoutError, ProtocolError))
378 |
379 | def _is_method_retryable(self, method: str) -> bool:
380 | """Checks if a given HTTP method should be retried upon, depending if
381 | it is included in the allowed_methods
382 | """
383 | if self.allowed_methods and method.upper() not in self.allowed_methods:
384 | return False
385 | return True
386 |
387 | def is_retry(
388 | self, method: str, status_code: int, has_retry_after: bool = False
389 | ) -> bool:
390 | """Is this method/status code retryable? (Based on allowlists and control
391 | variables such as the number of total retries to allow, whether to
392 | respect the Retry-After header, whether this header is present, and
393 | whether the returned status code is on the list of status codes to
394 | be retried upon on the presence of the aforementioned header)
395 | """
396 | if not self._is_method_retryable(method):
397 | return False
398 |
399 | if self.status_forcelist and status_code in self.status_forcelist:
400 | return True
401 |
402 | return bool(
403 | self.total
404 | and self.respect_retry_after_header
405 | and has_retry_after
406 | and (status_code in self.RETRY_AFTER_STATUS_CODES)
407 | )
408 |
409 | def is_exhausted(self) -> bool:
410 | """Are we out of retries?"""
411 | retry_counts = [
412 | x
413 | for x in (
414 | self.total,
415 | self.connect,
416 | self.read,
417 | self.redirect,
418 | self.status,
419 | self.other,
420 | )
421 | if x
422 | ]
423 | if not retry_counts:
424 | return False
425 |
426 | return min(retry_counts) < 0
427 |
428 | def increment(
429 | self,
430 | method: str | None = None,
431 | url: str | None = None,
432 | response: BaseHTTPResponse | None = None,
433 | error: Exception | None = None,
434 | _pool: ConnectionPool | None = None,
435 | _stacktrace: TracebackType | None = None,
436 | ) -> Self:
437 | """Return a new Retry object with incremented retry counters.
438 |
439 | :param response: A response object, or None, if the server did not
440 | return a response.
441 | :type response: :class:`~urllib3.response.BaseHTTPResponse`
442 | :param Exception error: An error encountered during the request, or
443 | None if the response was received successfully.
444 |
445 | :return: A new ``Retry`` object.
446 | """
447 | if self.total is False and error:
448 | # Disabled, indicate to re-raise the error.
449 | raise reraise(type(error), error, _stacktrace)
450 |
451 | total = self.total
452 | if total is not None:
453 | total -= 1
454 |
455 | connect = self.connect
456 | read = self.read
457 | redirect = self.redirect
458 | status_count = self.status
459 | other = self.other
460 | cause = "unknown"
461 | status = None
462 | redirect_location = None
463 |
464 | if error and self._is_connection_error(error):
465 | # Connect retry?
466 | if connect is False:
467 | raise reraise(type(error), error, _stacktrace)
468 | elif connect is not None:
469 | connect -= 1
470 |
471 | elif error and self._is_read_error(error):
472 | # Read retry?
473 | if read is False or method is None or not self._is_method_retryable(method):
474 | raise reraise(type(error), error, _stacktrace)
475 | elif read is not None:
476 | read -= 1
477 |
478 | elif error:
479 | # Other retry?
480 | if other is not None:
481 | other -= 1
482 |
483 | elif response and response.get_redirect_location():
484 | # Redirect retry?
485 | if redirect is not None:
486 | redirect -= 1
487 | cause = "too many redirects"
488 | response_redirect_location = response.get_redirect_location()
489 | if response_redirect_location:
490 | redirect_location = response_redirect_location
491 | status = response.status
492 |
493 | else:
494 | # Incrementing because of a server error like a 500 in
495 | # status_forcelist and the given method is in the allowed_methods
496 | cause = ResponseError.GENERIC_ERROR
497 | if response and response.status:
498 | if status_count is not None:
499 | status_count -= 1
500 | cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)
501 | status = response.status
502 |
503 | history = self.history + (
504 | RequestHistory(method, url, error, status, redirect_location),
505 | )
506 |
507 | new_retry = self.new(
508 | total=total,
509 | connect=connect,
510 | read=read,
511 | redirect=redirect,
512 | status=status_count,
513 | other=other,
514 | history=history,
515 | )
516 |
517 | if new_retry.is_exhausted():
518 | reason = error or ResponseError(cause)
519 | raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]
520 |
521 | log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)
522 |
523 | return new_retry
524 |
525 | def __repr__(self) -> str:
526 | return (
527 | f"{type(self).__name__}(total={self.total}, connect={self.connect}, "
528 | f"read={self.read}, redirect={self.redirect}, status={self.status})"
529 | )
530 |
531 |
532 | # For backwards compatibility (equivalent to pre-v1.9):
533 | Retry.DEFAULT = Retry(3)
534 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/click/shell_completion.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import re
3 | import typing as t
4 | from gettext import gettext as _
5 |
6 | from .core import Argument
7 | from .core import BaseCommand
8 | from .core import Context
9 | from .core import MultiCommand
10 | from .core import Option
11 | from .core import Parameter
12 | from .core import ParameterSource
13 | from .parser import split_arg_string
14 | from .utils import echo
15 |
16 |
17 | def shell_complete(
18 | cli: BaseCommand,
19 | ctx_args: t.MutableMapping[str, t.Any],
20 | prog_name: str,
21 | complete_var: str,
22 | instruction: str,
23 | ) -> int:
24 | """Perform shell completion for the given CLI program.
25 |
26 | :param cli: Command being called.
27 | :param ctx_args: Extra arguments to pass to
28 | ``cli.make_context``.
29 | :param prog_name: Name of the executable in the shell.
30 | :param complete_var: Name of the environment variable that holds
31 | the completion instruction.
32 | :param instruction: Value of ``complete_var`` with the completion
33 | instruction and shell, in the form ``instruction_shell``.
34 | :return: Status code to exit with.
35 | """
36 | shell, _, instruction = instruction.partition("_")
37 | comp_cls = get_completion_class(shell)
38 |
39 | if comp_cls is None:
40 | return 1
41 |
42 | comp = comp_cls(cli, ctx_args, prog_name, complete_var)
43 |
44 | if instruction == "source":
45 | echo(comp.source())
46 | return 0
47 |
48 | if instruction == "complete":
49 | echo(comp.complete())
50 | return 0
51 |
52 | return 1
53 |
54 |
55 | class CompletionItem:
56 | """Represents a completion value and metadata about the value. The
57 | default metadata is ``type`` to indicate special shell handling,
58 | and ``help`` if a shell supports showing a help string next to the
59 | value.
60 |
61 | Arbitrary parameters can be passed when creating the object, and
62 | accessed using ``item.attr``. If an attribute wasn't passed,
63 | accessing it returns ``None``.
64 |
65 | :param value: The completion suggestion.
66 | :param type: Tells the shell script to provide special completion
67 | support for the type. Click uses ``"dir"`` and ``"file"``.
68 | :param help: String shown next to the value if supported.
69 | :param kwargs: Arbitrary metadata. The built-in implementations
70 | don't use this, but custom type completions paired with custom
71 | shell support could use it.
72 | """
73 |
74 | __slots__ = ("value", "type", "help", "_info")
75 |
76 | def __init__(
77 | self,
78 | value: t.Any,
79 | type: str = "plain",
80 | help: t.Optional[str] = None,
81 | **kwargs: t.Any,
82 | ) -> None:
83 | self.value: t.Any = value
84 | self.type: str = type
85 | self.help: t.Optional[str] = help
86 | self._info = kwargs
87 |
88 | def __getattr__(self, name: str) -> t.Any:
89 | return self._info.get(name)
90 |
91 |
92 | # Only Bash >= 4.4 has the nosort option.
93 | _SOURCE_BASH = """\
94 | %(complete_func)s() {
95 | local IFS=$'\\n'
96 | local response
97 |
98 | response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \
99 | %(complete_var)s=bash_complete $1)
100 |
101 | for completion in $response; do
102 | IFS=',' read type value <<< "$completion"
103 |
104 | if [[ $type == 'dir' ]]; then
105 | COMPREPLY=()
106 | compopt -o dirnames
107 | elif [[ $type == 'file' ]]; then
108 | COMPREPLY=()
109 | compopt -o default
110 | elif [[ $type == 'plain' ]]; then
111 | COMPREPLY+=($value)
112 | fi
113 | done
114 |
115 | return 0
116 | }
117 |
118 | %(complete_func)s_setup() {
119 | complete -o nosort -F %(complete_func)s %(prog_name)s
120 | }
121 |
122 | %(complete_func)s_setup;
123 | """
124 |
125 | _SOURCE_ZSH = """\
126 | #compdef %(prog_name)s
127 |
128 | %(complete_func)s() {
129 | local -a completions
130 | local -a completions_with_descriptions
131 | local -a response
132 | (( ! $+commands[%(prog_name)s] )) && return 1
133 |
134 | response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \
135 | %(complete_var)s=zsh_complete %(prog_name)s)}")
136 |
137 | for type key descr in ${response}; do
138 | if [[ "$type" == "plain" ]]; then
139 | if [[ "$descr" == "_" ]]; then
140 | completions+=("$key")
141 | else
142 | completions_with_descriptions+=("$key":"$descr")
143 | fi
144 | elif [[ "$type" == "dir" ]]; then
145 | _path_files -/
146 | elif [[ "$type" == "file" ]]; then
147 | _path_files -f
148 | fi
149 | done
150 |
151 | if [ -n "$completions_with_descriptions" ]; then
152 | _describe -V unsorted completions_with_descriptions -U
153 | fi
154 |
155 | if [ -n "$completions" ]; then
156 | compadd -U -V unsorted -a completions
157 | fi
158 | }
159 |
160 | if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
161 | # autoload from fpath, call function directly
162 | %(complete_func)s "$@"
163 | else
164 | # eval/source/. command, register function for later
165 | compdef %(complete_func)s %(prog_name)s
166 | fi
167 | """
168 |
169 | _SOURCE_FISH = """\
170 | function %(complete_func)s;
171 | set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \
172 | COMP_CWORD=(commandline -t) %(prog_name)s);
173 |
174 | for completion in $response;
175 | set -l metadata (string split "," $completion);
176 |
177 | if test $metadata[1] = "dir";
178 | __fish_complete_directories $metadata[2];
179 | else if test $metadata[1] = "file";
180 | __fish_complete_path $metadata[2];
181 | else if test $metadata[1] = "plain";
182 | echo $metadata[2];
183 | end;
184 | end;
185 | end;
186 |
187 | complete --no-files --command %(prog_name)s --arguments \
188 | "(%(complete_func)s)";
189 | """
190 |
191 |
192 | class ShellComplete:
193 | """Base class for providing shell completion support. A subclass for
194 | a given shell will override attributes and methods to implement the
195 | completion instructions (``source`` and ``complete``).
196 |
197 | :param cli: Command being called.
198 | :param prog_name: Name of the executable in the shell.
199 | :param complete_var: Name of the environment variable that holds
200 | the completion instruction.
201 |
202 | .. versionadded:: 8.0
203 | """
204 |
205 | name: t.ClassVar[str]
206 | """Name to register the shell as with :func:`add_completion_class`.
207 | This is used in completion instructions (``{name}_source`` and
208 | ``{name}_complete``).
209 | """
210 |
211 | source_template: t.ClassVar[str]
212 | """Completion script template formatted by :meth:`source`. This must
213 | be provided by subclasses.
214 | """
215 |
216 | def __init__(
217 | self,
218 | cli: BaseCommand,
219 | ctx_args: t.MutableMapping[str, t.Any],
220 | prog_name: str,
221 | complete_var: str,
222 | ) -> None:
223 | self.cli = cli
224 | self.ctx_args = ctx_args
225 | self.prog_name = prog_name
226 | self.complete_var = complete_var
227 |
228 | @property
229 | def func_name(self) -> str:
230 | """The name of the shell function defined by the completion
231 | script.
232 | """
233 | safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII)
234 | return f"_{safe_name}_completion"
235 |
236 | def source_vars(self) -> t.Dict[str, t.Any]:
237 | """Vars for formatting :attr:`source_template`.
238 |
239 | By default this provides ``complete_func``, ``complete_var``,
240 | and ``prog_name``.
241 | """
242 | return {
243 | "complete_func": self.func_name,
244 | "complete_var": self.complete_var,
245 | "prog_name": self.prog_name,
246 | }
247 |
248 | def source(self) -> str:
249 | """Produce the shell script that defines the completion
250 | function. By default this ``%``-style formats
251 | :attr:`source_template` with the dict returned by
252 | :meth:`source_vars`.
253 | """
254 | return self.source_template % self.source_vars()
255 |
256 | def get_completion_args(self) -> t.Tuple[t.List[str], str]:
257 | """Use the env vars defined by the shell script to return a
258 | tuple of ``args, incomplete``. This must be implemented by
259 | subclasses.
260 | """
261 | raise NotImplementedError
262 |
263 | def get_completions(
264 | self, args: t.List[str], incomplete: str
265 | ) -> t.List[CompletionItem]:
266 | """Determine the context and last complete command or parameter
267 | from the complete args. Call that object's ``shell_complete``
268 | method to get the completions for the incomplete value.
269 |
270 | :param args: List of complete args before the incomplete value.
271 | :param incomplete: Value being completed. May be empty.
272 | """
273 | ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args)
274 | obj, incomplete = _resolve_incomplete(ctx, args, incomplete)
275 | return obj.shell_complete(ctx, incomplete)
276 |
277 | def format_completion(self, item: CompletionItem) -> str:
278 | """Format a completion item into the form recognized by the
279 | shell script. This must be implemented by subclasses.
280 |
281 | :param item: Completion item to format.
282 | """
283 | raise NotImplementedError
284 |
285 | def complete(self) -> str:
286 | """Produce the completion data to send back to the shell.
287 |
288 | By default this calls :meth:`get_completion_args`, gets the
289 | completions, then calls :meth:`format_completion` for each
290 | completion.
291 | """
292 | args, incomplete = self.get_completion_args()
293 | completions = self.get_completions(args, incomplete)
294 | out = [self.format_completion(item) for item in completions]
295 | return "\n".join(out)
296 |
297 |
298 | class BashComplete(ShellComplete):
299 | """Shell completion for Bash."""
300 |
301 | name = "bash"
302 | source_template = _SOURCE_BASH
303 |
304 | @staticmethod
305 | def _check_version() -> None:
306 | import subprocess
307 |
308 | output = subprocess.run(
309 | ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE
310 | )
311 | match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode())
312 |
313 | if match is not None:
314 | major, minor = match.groups()
315 |
316 | if major < "4" or major == "4" and minor < "4":
317 | echo(
318 | _(
319 | "Shell completion is not supported for Bash"
320 | " versions older than 4.4."
321 | ),
322 | err=True,
323 | )
324 | else:
325 | echo(
326 | _("Couldn't detect Bash version, shell completion is not supported."),
327 | err=True,
328 | )
329 |
330 | def source(self) -> str:
331 | self._check_version()
332 | return super().source()
333 |
334 | def get_completion_args(self) -> t.Tuple[t.List[str], str]:
335 | cwords = split_arg_string(os.environ["COMP_WORDS"])
336 | cword = int(os.environ["COMP_CWORD"])
337 | args = cwords[1:cword]
338 |
339 | try:
340 | incomplete = cwords[cword]
341 | except IndexError:
342 | incomplete = ""
343 |
344 | return args, incomplete
345 |
346 | def format_completion(self, item: CompletionItem) -> str:
347 | return f"{item.type},{item.value}"
348 |
349 |
350 | class ZshComplete(ShellComplete):
351 | """Shell completion for Zsh."""
352 |
353 | name = "zsh"
354 | source_template = _SOURCE_ZSH
355 |
356 | def get_completion_args(self) -> t.Tuple[t.List[str], str]:
357 | cwords = split_arg_string(os.environ["COMP_WORDS"])
358 | cword = int(os.environ["COMP_CWORD"])
359 | args = cwords[1:cword]
360 |
361 | try:
362 | incomplete = cwords[cword]
363 | except IndexError:
364 | incomplete = ""
365 |
366 | return args, incomplete
367 |
368 | def format_completion(self, item: CompletionItem) -> str:
369 | return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}"
370 |
371 |
372 | class FishComplete(ShellComplete):
373 | """Shell completion for Fish."""
374 |
375 | name = "fish"
376 | source_template = _SOURCE_FISH
377 |
378 | def get_completion_args(self) -> t.Tuple[t.List[str], str]:
379 | cwords = split_arg_string(os.environ["COMP_WORDS"])
380 | incomplete = os.environ["COMP_CWORD"]
381 | args = cwords[1:]
382 |
383 | # Fish stores the partial word in both COMP_WORDS and
384 | # COMP_CWORD, remove it from complete args.
385 | if incomplete and args and args[-1] == incomplete:
386 | args.pop()
387 |
388 | return args, incomplete
389 |
390 | def format_completion(self, item: CompletionItem) -> str:
391 | if item.help:
392 | return f"{item.type},{item.value}\t{item.help}"
393 |
394 | return f"{item.type},{item.value}"
395 |
396 |
397 | ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete])
398 |
399 |
400 | _available_shells: t.Dict[str, t.Type[ShellComplete]] = {
401 | "bash": BashComplete,
402 | "fish": FishComplete,
403 | "zsh": ZshComplete,
404 | }
405 |
406 |
407 | def add_completion_class(
408 | cls: ShellCompleteType, name: t.Optional[str] = None
409 | ) -> ShellCompleteType:
410 | """Register a :class:`ShellComplete` subclass under the given name.
411 | The name will be provided by the completion instruction environment
412 | variable during completion.
413 |
414 | :param cls: The completion class that will handle completion for the
415 | shell.
416 | :param name: Name to register the class under. Defaults to the
417 | class's ``name`` attribute.
418 | """
419 | if name is None:
420 | name = cls.name
421 |
422 | _available_shells[name] = cls
423 |
424 | return cls
425 |
426 |
427 | def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]:
428 | """Look up a registered :class:`ShellComplete` subclass by the name
429 | provided by the completion instruction environment variable. If the
430 | name isn't registered, returns ``None``.
431 |
432 | :param shell: Name the class is registered under.
433 | """
434 | return _available_shells.get(shell)
435 |
436 |
437 | def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
438 | """Determine if the given parameter is an argument that can still
439 | accept values.
440 |
441 | :param ctx: Invocation context for the command represented by the
442 | parsed complete args.
443 | :param param: Argument object being checked.
444 | """
445 | if not isinstance(param, Argument):
446 | return False
447 |
448 | assert param.name is not None
449 | # Will be None if expose_value is False.
450 | value = ctx.params.get(param.name)
451 | return (
452 | param.nargs == -1
453 | or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE
454 | or (
455 | param.nargs > 1
456 | and isinstance(value, (tuple, list))
457 | and len(value) < param.nargs
458 | )
459 | )
460 |
461 |
462 | def _start_of_option(ctx: Context, value: str) -> bool:
463 | """Check if the value looks like the start of an option."""
464 | if not value:
465 | return False
466 |
467 | c = value[0]
468 | return c in ctx._opt_prefixes
469 |
470 |
471 | def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool:
472 | """Determine if the given parameter is an option that needs a value.
473 |
474 | :param args: List of complete args before the incomplete value.
475 | :param param: Option object being checked.
476 | """
477 | if not isinstance(param, Option):
478 | return False
479 |
480 | if param.is_flag or param.count:
481 | return False
482 |
483 | last_option = None
484 |
485 | for index, arg in enumerate(reversed(args)):
486 | if index + 1 > param.nargs:
487 | break
488 |
489 | if _start_of_option(ctx, arg):
490 | last_option = arg
491 |
492 | return last_option is not None and last_option in param.opts
493 |
494 |
495 | def _resolve_context(
496 | cli: BaseCommand,
497 | ctx_args: t.MutableMapping[str, t.Any],
498 | prog_name: str,
499 | args: t.List[str],
500 | ) -> Context:
501 | """Produce the context hierarchy starting with the command and
502 | traversing the complete arguments. This only follows the commands,
503 | it doesn't trigger input prompts or callbacks.
504 |
505 | :param cli: Command being called.
506 | :param prog_name: Name of the executable in the shell.
507 | :param args: List of complete args before the incomplete value.
508 | """
509 | ctx_args["resilient_parsing"] = True
510 | ctx = cli.make_context(prog_name, args.copy(), **ctx_args)
511 | args = ctx.protected_args + ctx.args
512 |
513 | while args:
514 | command = ctx.command
515 |
516 | if isinstance(command, MultiCommand):
517 | if not command.chain:
518 | name, cmd, args = command.resolve_command(ctx, args)
519 |
520 | if cmd is None:
521 | return ctx
522 |
523 | ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True)
524 | args = ctx.protected_args + ctx.args
525 | else:
526 | sub_ctx = ctx
527 |
528 | while args:
529 | name, cmd, args = command.resolve_command(ctx, args)
530 |
531 | if cmd is None:
532 | return ctx
533 |
534 | sub_ctx = cmd.make_context(
535 | name,
536 | args,
537 | parent=ctx,
538 | allow_extra_args=True,
539 | allow_interspersed_args=False,
540 | resilient_parsing=True,
541 | )
542 | args = sub_ctx.args
543 |
544 | ctx = sub_ctx
545 | args = [*sub_ctx.protected_args, *sub_ctx.args]
546 | else:
547 | break
548 |
549 | return ctx
550 |
551 |
552 | def _resolve_incomplete(
553 | ctx: Context, args: t.List[str], incomplete: str
554 | ) -> t.Tuple[t.Union[BaseCommand, Parameter], str]:
555 | """Find the Click object that will handle the completion of the
556 | incomplete value. Return the object and the incomplete value.
557 |
558 | :param ctx: Invocation context for the command represented by
559 | the parsed complete args.
560 | :param args: List of complete args before the incomplete value.
561 | :param incomplete: Value being completed. May be empty.
562 | """
563 | # Different shells treat an "=" between a long option name and
564 | # value differently. Might keep the value joined, return the "="
565 | # as a separate item, or return the split name and value. Always
566 | # split and discard the "=" to make completion easier.
567 | if incomplete == "=":
568 | incomplete = ""
569 | elif "=" in incomplete and _start_of_option(ctx, incomplete):
570 | name, _, incomplete = incomplete.partition("=")
571 | args.append(name)
572 |
573 | # The "--" marker tells Click to stop treating values as options
574 | # even if they start with the option character. If it hasn't been
575 | # given and the incomplete arg looks like an option, the current
576 | # command will provide option name completions.
577 | if "--" not in args and _start_of_option(ctx, incomplete):
578 | return ctx.command, incomplete
579 |
580 | params = ctx.command.get_params(ctx)
581 |
582 | # If the last complete arg is an option name with an incomplete
583 | # value, the option will provide value completions.
584 | for param in params:
585 | if _is_incomplete_option(ctx, args, param):
586 | return param, incomplete
587 |
588 | # It's not an option name or value. The first argument without a
589 | # parsed value will provide value completions.
590 | for param in params:
591 | if _is_incomplete_argument(ctx, param):
592 | return param, incomplete
593 |
594 | # There were no unparsed arguments, the command may be a group that
595 | # will provide command name completions.
596 | return ctx.command, incomplete
597 |
```