This is page 156 of 168. Use http://codebase.md/romanshablio/mcp_server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .DS_Store
├── .venv
│ ├── __pycache__
│ │ └── hello.cpython-312.pyc
│ ├── bin
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── Activate.ps1
│ │ ├── flask
│ │ ├── normalizer
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.12
│ │ ├── python
│ │ ├── python3
│ │ └── python3.12
│ ├── hello.py
│ ├── lib
│ │ └── python3.12
│ │ └── site-packages
│ │ ├── beautifulsoup4-4.12.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ ├── AUTHORS
│ │ │ │ └── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── blinker
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _utilities.cpython-312.pyc
│ │ │ │ └── base.cpython-312.pyc
│ │ │ ├── _utilities.py
│ │ │ ├── base.py
│ │ │ └── py.typed
│ │ ├── blinker-1.8.2.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── bs4
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── css.cpython-312.pyc
│ │ │ │ ├── dammit.cpython-312.pyc
│ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ ├── element.cpython-312.pyc
│ │ │ │ └── formatter.cpython-312.pyc
│ │ │ ├── builder
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _html5lib.cpython-312.pyc
│ │ │ │ │ ├── _htmlparser.cpython-312.pyc
│ │ │ │ │ └── _lxml.cpython-312.pyc
│ │ │ │ ├── _html5lib.py
│ │ │ │ ├── _htmlparser.py
│ │ │ │ └── _lxml.py
│ │ │ ├── css.py
│ │ │ ├── dammit.py
│ │ │ ├── diagnose.py
│ │ │ ├── element.py
│ │ │ ├── formatter.py
│ │ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── test_builder_registry.cpython-312.pyc
│ │ │ │ ├── test_builder.cpython-312.pyc
│ │ │ │ ├── test_css.cpython-312.pyc
│ │ │ │ ├── test_dammit.cpython-312.pyc
│ │ │ │ ├── test_docs.cpython-312.pyc
│ │ │ │ ├── test_element.cpython-312.pyc
│ │ │ │ ├── test_formatter.cpython-312.pyc
│ │ │ │ ├── test_fuzz.cpython-312.pyc
│ │ │ │ ├── test_html5lib.cpython-312.pyc
│ │ │ │ ├── test_htmlparser.cpython-312.pyc
│ │ │ │ ├── test_lxml.cpython-312.pyc
│ │ │ │ ├── test_navigablestring.cpython-312.pyc
│ │ │ │ ├── test_pageelement.cpython-312.pyc
│ │ │ │ ├── test_soup.cpython-312.pyc
│ │ │ │ ├── test_tag.cpython-312.pyc
│ │ │ │ └── test_tree.cpython-312.pyc
│ │ │ ├── fuzz
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase
│ │ │ │ ├── crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase
│ │ │ │ └── crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase
│ │ │ ├── test_builder_registry.py
│ │ │ ├── test_builder.py
│ │ │ ├── test_css.py
│ │ │ ├── test_dammit.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_element.py
│ │ │ ├── test_formatter.py
│ │ │ ├── test_fuzz.py
│ │ │ ├── test_html5lib.py
│ │ │ ├── test_htmlparser.py
│ │ │ ├── test_lxml.py
│ │ │ ├── test_navigablestring.py
│ │ │ ├── test_pageelement.py
│ │ │ ├── test_soup.py
│ │ │ ├── test_tag.py
│ │ │ └── test_tree.py
│ │ ├── certifi
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── core.cpython-312.pyc
│ │ │ ├── cacert.pem
│ │ │ ├── core.py
│ │ │ └── py.typed
│ │ ├── certifi-2024.8.30.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── charset_normalizer
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── cd.cpython-312.pyc
│ │ │ │ ├── constant.cpython-312.pyc
│ │ │ │ ├── legacy.cpython-312.pyc
│ │ │ │ ├── md.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── version.cpython-312.pyc
│ │ │ ├── api.py
│ │ │ ├── cd.py
│ │ │ ├── cli
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── __main__.cpython-312.pyc
│ │ │ ├── constant.py
│ │ │ ├── legacy.py
│ │ │ ├── md__mypyc.cpython-312-darwin.so
│ │ │ ├── md.cpython-312-darwin.so
│ │ │ ├── md.py
│ │ │ ├── models.py
│ │ │ ├── py.typed
│ │ │ ├── utils.py
│ │ │ └── version.py
│ │ ├── charset_normalizer-3.4.0.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── click
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ ├── _termui_impl.cpython-312.pyc
│ │ │ │ ├── _textwrap.cpython-312.pyc
│ │ │ │ ├── _winconsole.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── decorators.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formatting.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── shell_completion.cpython-312.pyc
│ │ │ │ ├── termui.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── types.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── _compat.py
│ │ │ ├── _termui_impl.py
│ │ │ ├── _textwrap.py
│ │ │ ├── _winconsole.py
│ │ │ ├── core.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── formatting.py
│ │ │ ├── globals.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── shell_completion.py
│ │ │ ├── termui.py
│ │ │ ├── testing.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ ├── click-8.1.7.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.rst
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── fake_useragent
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ ├── fake.cpython-312.pyc
│ │ │ │ ├── log.cpython-312.pyc
│ │ │ │ ├── settings.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── data
│ │ │ │ └── browsers.json
│ │ │ ├── errors.py
│ │ │ ├── fake.py
│ │ │ ├── log.py
│ │ │ ├── settings.py
│ │ │ └── utils.py
│ │ ├── fake_useragent-1.5.1.dist-info
│ │ │ ├── AUTHORS
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── flask
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ ├── cli.cpython-312.pyc
│ │ │ │ ├── config.cpython-312.pyc
│ │ │ │ ├── ctx.cpython-312.pyc
│ │ │ │ ├── debughelpers.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── helpers.cpython-312.pyc
│ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── signals.cpython-312.pyc
│ │ │ │ ├── templating.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── typing.cpython-312.pyc
│ │ │ │ ├── views.cpython-312.pyc
│ │ │ │ └── wrappers.cpython-312.pyc
│ │ │ ├── app.py
│ │ │ ├── blueprints.py
│ │ │ ├── cli.py
│ │ │ ├── config.py
│ │ │ ├── ctx.py
│ │ │ ├── debughelpers.py
│ │ │ ├── globals.py
│ │ │ ├── helpers.py
│ │ │ ├── json
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ └── tag.cpython-312.pyc
│ │ │ │ ├── provider.py
│ │ │ │ └── tag.py
│ │ │ ├── logging.py
│ │ │ ├── py.typed
│ │ │ ├── sansio
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ │ └── scaffold.cpython-312.pyc
│ │ │ │ ├── app.py
│ │ │ │ ├── blueprints.py
│ │ │ │ ├── README.md
│ │ │ │ └── scaffold.py
│ │ │ ├── sessions.py
│ │ │ ├── signals.py
│ │ │ ├── templating.py
│ │ │ ├── testing.py
│ │ │ ├── typing.py
│ │ │ ├── views.py
│ │ │ └── wrappers.py
│ │ ├── flask-3.0.3.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── idna
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ ├── codec.py
│ │ │ ├── compat.py
│ │ │ ├── core.py
│ │ │ ├── idnadata.py
│ │ │ ├── intranges.py
│ │ │ ├── package_data.py
│ │ │ ├── py.typed
│ │ │ └── uts46data.py
│ │ ├── idna-3.10.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── itsdangerous
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ ├── exc.cpython-312.pyc
│ │ │ │ ├── serializer.cpython-312.pyc
│ │ │ │ ├── signer.cpython-312.pyc
│ │ │ │ ├── timed.cpython-312.pyc
│ │ │ │ └── url_safe.cpython-312.pyc
│ │ │ ├── _json.py
│ │ │ ├── encoding.py
│ │ │ ├── exc.py
│ │ │ ├── py.typed
│ │ │ ├── serializer.py
│ │ │ ├── signer.py
│ │ │ ├── timed.py
│ │ │ └── url_safe.py
│ │ ├── itsdangerous-2.2.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── jinja2
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _identifier.cpython-312.pyc
│ │ │ │ ├── async_utils.cpython-312.pyc
│ │ │ │ ├── bccache.cpython-312.pyc
│ │ │ │ ├── compiler.cpython-312.pyc
│ │ │ │ ├── constants.cpython-312.pyc
│ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ ├── defaults.cpython-312.pyc
│ │ │ │ ├── environment.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ ├── filters.cpython-312.pyc
│ │ │ │ ├── idtracking.cpython-312.pyc
│ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ ├── loaders.cpython-312.pyc
│ │ │ │ ├── meta.cpython-312.pyc
│ │ │ │ ├── nativetypes.cpython-312.pyc
│ │ │ │ ├── nodes.cpython-312.pyc
│ │ │ │ ├── optimizer.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── runtime.cpython-312.pyc
│ │ │ │ ├── sandbox.cpython-312.pyc
│ │ │ │ ├── tests.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── visitor.cpython-312.pyc
│ │ │ ├── _identifier.py
│ │ │ ├── async_utils.py
│ │ │ ├── bccache.py
│ │ │ ├── compiler.py
│ │ │ ├── constants.py
│ │ │ ├── debug.py
│ │ │ ├── defaults.py
│ │ │ ├── environment.py
│ │ │ ├── exceptions.py
│ │ │ ├── ext.py
│ │ │ ├── filters.py
│ │ │ ├── idtracking.py
│ │ │ ├── lexer.py
│ │ │ ├── loaders.py
│ │ │ ├── meta.py
│ │ │ ├── nativetypes.py
│ │ │ ├── nodes.py
│ │ │ ├── optimizer.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── runtime.py
│ │ │ ├── sandbox.py
│ │ │ ├── tests.py
│ │ │ ├── utils.py
│ │ │ └── visitor.py
│ │ ├── jinja2-3.1.4.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── lxml
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _elementpath.cpython-312.pyc
│ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ ├── cssselect.cpython-312.pyc
│ │ │ │ ├── doctestcompare.cpython-312.pyc
│ │ │ │ ├── ElementInclude.cpython-312.pyc
│ │ │ │ ├── pyclasslookup.cpython-312.pyc
│ │ │ │ ├── sax.cpython-312.pyc
│ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ ├── _elementpath.cpython-312-darwin.so
│ │ │ ├── _elementpath.py
│ │ │ ├── apihelpers.pxi
│ │ │ ├── builder.cpython-312-darwin.so
│ │ │ ├── builder.py
│ │ │ ├── classlookup.pxi
│ │ │ ├── cleanup.pxi
│ │ │ ├── cssselect.py
│ │ │ ├── debug.pxi
│ │ │ ├── docloader.pxi
│ │ │ ├── doctestcompare.py
│ │ │ ├── dtd.pxi
│ │ │ ├── ElementInclude.py
│ │ │ ├── etree_api.h
│ │ │ ├── etree.cpython-312-darwin.so
│ │ │ ├── etree.h
│ │ │ ├── etree.pyx
│ │ │ ├── extensions.pxi
│ │ │ ├── html
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _diffcommand.cpython-312.pyc
│ │ │ │ │ ├── _html5builder.cpython-312.pyc
│ │ │ │ │ ├── _setmixin.cpython-312.pyc
│ │ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ │ ├── clean.cpython-312.pyc
│ │ │ │ │ ├── defs.cpython-312.pyc
│ │ │ │ │ ├── diff.cpython-312.pyc
│ │ │ │ │ ├── ElementSoup.cpython-312.pyc
│ │ │ │ │ ├── formfill.cpython-312.pyc
│ │ │ │ │ ├── html5parser.cpython-312.pyc
│ │ │ │ │ ├── soupparser.cpython-312.pyc
│ │ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ │ ├── _diffcommand.py
│ │ │ │ ├── _html5builder.py
│ │ │ │ ├── _setmixin.py
│ │ │ │ ├── builder.py
│ │ │ │ ├── clean.py
│ │ │ │ ├── defs.py
│ │ │ │ ├── diff.cpython-312-darwin.so
│ │ │ │ ├── diff.py
│ │ │ │ ├── ElementSoup.py
│ │ │ │ ├── formfill.py
│ │ │ │ ├── html5parser.py
│ │ │ │ ├── soupparser.py
│ │ │ │ └── usedoctest.py
│ │ │ ├── includes
│ │ │ │ ├── __init__.pxd
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── c14n.pxd
│ │ │ │ ├── config.pxd
│ │ │ │ ├── dtdvalid.pxd
│ │ │ │ ├── etree_defs.h
│ │ │ │ ├── etreepublic.pxd
│ │ │ │ ├── extlibs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── libcharset.h
│ │ │ │ │ ├── localcharset.h
│ │ │ │ │ ├── zconf.h
│ │ │ │ │ └── zlib.h
│ │ │ │ ├── htmlparser.pxd
│ │ │ │ ├── libexslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── exslt.h
│ │ │ │ │ ├── exsltconfig.h
│ │ │ │ │ └── exsltexports.h
│ │ │ │ ├── libxml
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── c14n.h
│ │ │ │ │ ├── catalog.h
│ │ │ │ │ ├── chvalid.h
│ │ │ │ │ ├── debugXML.h
│ │ │ │ │ ├── dict.h
│ │ │ │ │ ├── encoding.h
│ │ │ │ │ ├── entities.h
│ │ │ │ │ ├── globals.h
│ │ │ │ │ ├── hash.h
│ │ │ │ │ ├── HTMLparser.h
│ │ │ │ │ ├── HTMLtree.h
│ │ │ │ │ ├── list.h
│ │ │ │ │ ├── nanoftp.h
│ │ │ │ │ ├── nanohttp.h
│ │ │ │ │ ├── parser.h
│ │ │ │ │ ├── parserInternals.h
│ │ │ │ │ ├── relaxng.h
│ │ │ │ │ ├── SAX.h
│ │ │ │ │ ├── SAX2.h
│ │ │ │ │ ├── schemasInternals.h
│ │ │ │ │ ├── schematron.h
│ │ │ │ │ ├── threads.h
│ │ │ │ │ ├── tree.h
│ │ │ │ │ ├── uri.h
│ │ │ │ │ ├── valid.h
│ │ │ │ │ ├── xinclude.h
│ │ │ │ │ ├── xlink.h
│ │ │ │ │ ├── xmlautomata.h
│ │ │ │ │ ├── xmlerror.h
│ │ │ │ │ ├── xmlexports.h
│ │ │ │ │ ├── xmlIO.h
│ │ │ │ │ ├── xmlmemory.h
│ │ │ │ │ ├── xmlmodule.h
│ │ │ │ │ ├── xmlreader.h
│ │ │ │ │ ├── xmlregexp.h
│ │ │ │ │ ├── xmlsave.h
│ │ │ │ │ ├── xmlschemas.h
│ │ │ │ │ ├── xmlschemastypes.h
│ │ │ │ │ ├── xmlstring.h
│ │ │ │ │ ├── xmlunicode.h
│ │ │ │ │ ├── xmlversion.h
│ │ │ │ │ ├── xmlwriter.h
│ │ │ │ │ ├── xpath.h
│ │ │ │ │ ├── xpathInternals.h
│ │ │ │ │ └── xpointer.h
│ │ │ │ ├── libxslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── attributes.h
│ │ │ │ │ ├── documents.h
│ │ │ │ │ ├── extensions.h
│ │ │ │ │ ├── extra.h
│ │ │ │ │ ├── functions.h
│ │ │ │ │ ├── imports.h
│ │ │ │ │ ├── keys.h
│ │ │ │ │ ├── namespaces.h
│ │ │ │ │ ├── numbersInternals.h
│ │ │ │ │ ├── pattern.h
│ │ │ │ │ ├── preproc.h
│ │ │ │ │ ├── security.h
│ │ │ │ │ ├── templates.h
│ │ │ │ │ ├── transform.h
│ │ │ │ │ ├── variables.h
│ │ │ │ │ ├── xslt.h
│ │ │ │ │ ├── xsltconfig.h
│ │ │ │ │ ├── xsltexports.h
│ │ │ │ │ ├── xsltInternals.h
│ │ │ │ │ ├── xsltlocale.h
│ │ │ │ │ └── xsltutils.h
│ │ │ │ ├── lxml-version.h
│ │ │ │ ├── relaxng.pxd
│ │ │ │ ├── schematron.pxd
│ │ │ │ ├── tree.pxd
│ │ │ │ ├── uri.pxd
│ │ │ │ ├── xinclude.pxd
│ │ │ │ ├── xmlerror.pxd
│ │ │ │ ├── xmlparser.pxd
│ │ │ │ ├── xmlschema.pxd
│ │ │ │ ├── xpath.pxd
│ │ │ │ └── xslt.pxd
│ │ │ ├── isoschematron
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ └── resources
│ │ │ │ ├── rng
│ │ │ │ │ └── iso-schematron.rng
│ │ │ │ └── xsl
│ │ │ │ ├── iso-schematron-xslt1
│ │ │ │ │ ├── iso_abstract_expand.xsl
│ │ │ │ │ ├── iso_dsdl_include.xsl
│ │ │ │ │ ├── iso_schematron_message.xsl
│ │ │ │ │ ├── iso_schematron_skeleton_for_xslt1.xsl
│ │ │ │ │ ├── iso_svrl_for_xslt1.xsl
│ │ │ │ │ └── readme.txt
│ │ │ │ ├── RNG2Schtrn.xsl
│ │ │ │ └── XSD2Schtrn.xsl
│ │ │ ├── iterparse.pxi
│ │ │ ├── lxml.etree_api.h
│ │ │ ├── lxml.etree.h
│ │ │ ├── nsclasses.pxi
│ │ │ ├── objectify.cpython-312-darwin.so
│ │ │ ├── objectify.pyx
│ │ │ ├── objectpath.pxi
│ │ │ ├── parser.pxi
│ │ │ ├── parsertarget.pxi
│ │ │ ├── proxy.pxi
│ │ │ ├── public-api.pxi
│ │ │ ├── pyclasslookup.py
│ │ │ ├── readonlytree.pxi
│ │ │ ├── relaxng.pxi
│ │ │ ├── sax.cpython-312-darwin.so
│ │ │ ├── sax.py
│ │ │ ├── saxparser.pxi
│ │ │ ├── schematron.pxi
│ │ │ ├── serializer.pxi
│ │ │ ├── usedoctest.py
│ │ │ ├── xinclude.pxi
│ │ │ ├── xmlerror.pxi
│ │ │ ├── xmlid.pxi
│ │ │ ├── xmlschema.pxi
│ │ │ ├── xpath.pxi
│ │ │ ├── xslt.pxi
│ │ │ └── xsltext.pxi
│ │ ├── lxml-5.3.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── LICENSES.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── markupsafe
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── _native.cpython-312.pyc
│ │ │ ├── _native.py
│ │ │ ├── _speedups.c
│ │ │ ├── _speedups.cpython-312-darwin.so
│ │ │ ├── _speedups.pyi
│ │ │ └── py.typed
│ │ ├── MarkupSafe-3.0.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── pip
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pip-runner__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── __pip-runner__.cpython-312.pyc
│ │ │ ├── _internal
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── build_env.cpython-312.pyc
│ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ ├── pyproject.cpython-312.pyc
│ │ │ │ │ ├── self_outdated_check.cpython-312.pyc
│ │ │ │ │ └── wheel_builder.cpython-312.pyc
│ │ │ │ ├── build_env.py
│ │ │ │ ├── cache.py
│ │ │ │ ├── cli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── autocompletion.cpython-312.pyc
│ │ │ │ │ │ ├── base_command.cpython-312.pyc
│ │ │ │ │ │ ├── cmdoptions.cpython-312.pyc
│ │ │ │ │ │ ├── command_context.cpython-312.pyc
│ │ │ │ │ │ ├── index_command.cpython-312.pyc
│ │ │ │ │ │ ├── main_parser.cpython-312.pyc
│ │ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bars.cpython-312.pyc
│ │ │ │ │ │ ├── req_command.cpython-312.pyc
│ │ │ │ │ │ ├── spinners.cpython-312.pyc
│ │ │ │ │ │ └── status_codes.cpython-312.pyc
│ │ │ │ │ ├── autocompletion.py
│ │ │ │ │ ├── base_command.py
│ │ │ │ │ ├── cmdoptions.py
│ │ │ │ │ ├── command_context.py
│ │ │ │ │ ├── index_command.py
│ │ │ │ │ ├── main_parser.py
│ │ │ │ │ ├── main.py
│ │ │ │ │ ├── parser.py
│ │ │ │ │ ├── progress_bars.py
│ │ │ │ │ ├── req_command.py
│ │ │ │ │ ├── spinners.py
│ │ │ │ │ └── status_codes.py
│ │ │ │ ├── commands
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── completion.cpython-312.pyc
│ │ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ ├── hash.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── inspect.cpython-312.pyc
│ │ │ │ │ │ ├── install.cpython-312.pyc
│ │ │ │ │ │ ├── list.cpython-312.pyc
│ │ │ │ │ │ ├── search.cpython-312.pyc
│ │ │ │ │ │ ├── show.cpython-312.pyc
│ │ │ │ │ │ ├── uninstall.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── completion.py
│ │ │ │ │ ├── configuration.py
│ │ │ │ │ ├── debug.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── hash.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── inspect.py
│ │ │ │ │ ├── install.py
│ │ │ │ │ ├── list.py
│ │ │ │ │ ├── search.py
│ │ │ │ │ ├── show.py
│ │ │ │ │ ├── uninstall.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── configuration.py
│ │ │ │ ├── distributions
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── installed.cpython-312.pyc
│ │ │ │ │ │ ├── sdist.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── installed.py
│ │ │ │ │ ├── sdist.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── index
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── collector.cpython-312.pyc
│ │ │ │ │ │ ├── package_finder.cpython-312.pyc
│ │ │ │ │ │ └── sources.cpython-312.pyc
│ │ │ │ │ ├── collector.py
│ │ │ │ │ ├── package_finder.py
│ │ │ │ │ └── sources.py
│ │ │ │ ├── locations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _distutils.cpython-312.pyc
│ │ │ │ │ │ ├── _sysconfig.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── _distutils.py
│ │ │ │ │ ├── _sysconfig.py
│ │ │ │ │ └── base.py
│ │ │ │ ├── main.py
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ └── pkg_resources.cpython-312.pyc
│ │ │ │ │ ├── _json.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── importlib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ │ ├── _dists.cpython-312.pyc
│ │ │ │ │ │ │ └── _envs.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.py
│ │ │ │ │ │ ├── _dists.py
│ │ │ │ │ │ └── _envs.py
│ │ │ │ │ └── pkg_resources.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── candidate.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url.cpython-312.pyc
│ │ │ │ │ │ ├── format_control.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── installation_report.cpython-312.pyc
│ │ │ │ │ │ ├── link.cpython-312.pyc
│ │ │ │ │ │ ├── scheme.cpython-312.pyc
│ │ │ │ │ │ ├── search_scope.cpython-312.pyc
│ │ │ │ │ │ ├── selection_prefs.cpython-312.pyc
│ │ │ │ │ │ ├── target_python.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── candidate.py
│ │ │ │ │ ├── direct_url.py
│ │ │ │ │ ├── format_control.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── installation_report.py
│ │ │ │ │ ├── link.py
│ │ │ │ │ ├── scheme.py
│ │ │ │ │ ├── search_scope.py
│ │ │ │ │ ├── selection_prefs.py
│ │ │ │ │ ├── target_python.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── network
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── lazy_wheel.cpython-312.pyc
│ │ │ │ │ │ ├── session.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── xmlrpc.cpython-312.pyc
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── lazy_wheel.py
│ │ │ │ │ ├── session.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── xmlrpc.py
│ │ │ │ ├── operations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ └── prepare.cpython-312.pyc
│ │ │ │ │ ├── build
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── build_tracker.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_legacy.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── build_tracker.py
│ │ │ │ │ │ ├── metadata_editable.py
│ │ │ │ │ │ ├── metadata_legacy.py
│ │ │ │ │ │ ├── metadata.py
│ │ │ │ │ │ ├── wheel_editable.py
│ │ │ │ │ │ ├── wheel_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── install
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── editable_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── editable_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ └── prepare.py
│ │ │ │ ├── pyproject.py
│ │ │ │ ├── req
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── constructors.cpython-312.pyc
│ │ │ │ │ │ ├── req_file.cpython-312.pyc
│ │ │ │ │ │ ├── req_install.cpython-312.pyc
│ │ │ │ │ │ ├── req_set.cpython-312.pyc
│ │ │ │ │ │ └── req_uninstall.cpython-312.pyc
│ │ │ │ │ ├── constructors.py
│ │ │ │ │ ├── req_file.py
│ │ │ │ │ ├── req_install.py
│ │ │ │ │ ├── req_set.py
│ │ │ │ │ └── req_uninstall.py
│ │ │ │ ├── resolution
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ │ └── resolver.py
│ │ │ │ │ └── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── candidates.cpython-312.pyc
│ │ │ │ │ │ ├── factory.cpython-312.pyc
│ │ │ │ │ │ ├── found_candidates.cpython-312.pyc
│ │ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ │ ├── reporter.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── candidates.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── found_candidates.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ ├── reporter.py
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ └── resolver.py
│ │ │ │ ├── self_outdated_check.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _jaraco_text.cpython-312.pyc
│ │ │ │ │ │ ├── _log.cpython-312.pyc
│ │ │ │ │ │ ├── appdirs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── compatibility_tags.cpython-312.pyc
│ │ │ │ │ │ ├── datetime.cpython-312.pyc
│ │ │ │ │ │ ├── deprecation.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url_helpers.cpython-312.pyc
│ │ │ │ │ │ ├── egg_link.cpython-312.pyc
│ │ │ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ │ │ ├── entrypoints.cpython-312.pyc
│ │ │ │ │ │ ├── filesystem.cpython-312.pyc
│ │ │ │ │ │ ├── filetypes.cpython-312.pyc
│ │ │ │ │ │ ├── glibc.cpython-312.pyc
│ │ │ │ │ │ ├── hashes.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── misc.cpython-312.pyc
│ │ │ │ │ │ ├── packaging.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── setuptools_build.cpython-312.pyc
│ │ │ │ │ │ ├── subprocess.cpython-312.pyc
│ │ │ │ │ │ ├── temp_dir.cpython-312.pyc
│ │ │ │ │ │ ├── unpacking.cpython-312.pyc
│ │ │ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ │ │ ├── virtualenv.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── _jaraco_text.py
│ │ │ │ │ ├── _log.py
│ │ │ │ │ ├── appdirs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── compatibility_tags.py
│ │ │ │ │ ├── datetime.py
│ │ │ │ │ ├── deprecation.py
│ │ │ │ │ ├── direct_url_helpers.py
│ │ │ │ │ ├── egg_link.py
│ │ │ │ │ ├── encoding.py
│ │ │ │ │ ├── entrypoints.py
│ │ │ │ │ ├── filesystem.py
│ │ │ │ │ ├── filetypes.py
│ │ │ │ │ ├── glibc.py
│ │ │ │ │ ├── hashes.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── misc.py
│ │ │ │ │ ├── packaging.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── setuptools_build.py
│ │ │ │ │ ├── subprocess.py
│ │ │ │ │ ├── temp_dir.py
│ │ │ │ │ ├── unpacking.py
│ │ │ │ │ ├── urls.py
│ │ │ │ │ ├── virtualenv.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── vcs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── bazaar.cpython-312.pyc
│ │ │ │ │ │ ├── git.cpython-312.pyc
│ │ │ │ │ │ ├── mercurial.cpython-312.pyc
│ │ │ │ │ │ ├── subversion.cpython-312.pyc
│ │ │ │ │ │ └── versioncontrol.cpython-312.pyc
│ │ │ │ │ ├── bazaar.py
│ │ │ │ │ ├── git.py
│ │ │ │ │ ├── mercurial.py
│ │ │ │ │ ├── subversion.py
│ │ │ │ │ └── versioncontrol.py
│ │ │ │ └── wheel_builder.py
│ │ │ ├── _vendor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ └── typing_extensions.cpython-312.pyc
│ │ │ │ ├── cachecontrol
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _cmd.cpython-312.pyc
│ │ │ │ │ │ ├── adapter.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── controller.cpython-312.pyc
│ │ │ │ │ │ ├── filewrapper.cpython-312.pyc
│ │ │ │ │ │ ├── heuristics.cpython-312.pyc
│ │ │ │ │ │ ├── serialize.cpython-312.pyc
│ │ │ │ │ │ └── wrapper.cpython-312.pyc
│ │ │ │ │ ├── _cmd.py
│ │ │ │ │ ├── adapter.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── caches
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── file_cache.cpython-312.pyc
│ │ │ │ │ │ │ └── redis_cache.cpython-312.pyc
│ │ │ │ │ │ ├── file_cache.py
│ │ │ │ │ │ └── redis_cache.py
│ │ │ │ │ ├── controller.py
│ │ │ │ │ ├── filewrapper.py
│ │ │ │ │ ├── heuristics.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── serialize.py
│ │ │ │ │ └── wrapper.py
│ │ │ │ ├── certifi
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── core.cpython-312.pyc
│ │ │ │ │ ├── cacert.pem
│ │ │ │ │ ├── core.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── distlib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── database.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── locators.cpython-312.pyc
│ │ │ │ │ │ ├── manifest.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── resources.cpython-312.pyc
│ │ │ │ │ │ ├── scripts.cpython-312.pyc
│ │ │ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── database.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── locators.py
│ │ │ │ │ ├── manifest.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ ├── scripts.py
│ │ │ │ │ ├── t32.exe
│ │ │ │ │ ├── t64-arm.exe
│ │ │ │ │ ├── t64.exe
│ │ │ │ │ ├── util.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ ├── w32.exe
│ │ │ │ │ ├── w64-arm.exe
│ │ │ │ │ ├── w64.exe
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── distro
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── distro.cpython-312.pyc
│ │ │ │ │ ├── distro.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── idna
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ │ │ ├── codec.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── core.py
│ │ │ │ │ ├── idnadata.py
│ │ │ │ │ ├── intranges.py
│ │ │ │ │ ├── package_data.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ └── uts46data.py
│ │ │ │ ├── msgpack
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ │ │ └── fallback.cpython-312.pyc
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── ext.py
│ │ │ │ │ └── fallback.py
│ │ │ │ ├── packaging
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _elffile.cpython-312.pyc
│ │ │ │ │ │ ├── _manylinux.cpython-312.pyc
│ │ │ │ │ │ ├── _musllinux.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _structures.cpython-312.pyc
│ │ │ │ │ │ ├── _tokenizer.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ ├── specifiers.cpython-312.pyc
│ │ │ │ │ │ ├── tags.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── version.cpython-312.pyc
│ │ │ │ │ ├── _elffile.py
│ │ │ │ │ ├── _manylinux.py
│ │ │ │ │ ├── _musllinux.py
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _structures.py
│ │ │ │ │ ├── _tokenizer.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ ├── specifiers.py
│ │ │ │ │ ├── tags.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── version.py
│ │ │ │ ├── pkg_resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── platformdirs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── android.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── macos.cpython-312.pyc
│ │ │ │ │ │ ├── unix.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── windows.cpython-312.pyc
│ │ │ │ │ ├── android.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── unix.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── pygments
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── cmdline.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── filter.cpython-312.pyc
│ │ │ │ │ │ ├── formatter.cpython-312.pyc
│ │ │ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ │ │ ├── modeline.cpython-312.pyc
│ │ │ │ │ │ ├── plugin.cpython-312.pyc
│ │ │ │ │ │ ├── regexopt.cpython-312.pyc
│ │ │ │ │ │ ├── scanner.cpython-312.pyc
│ │ │ │ │ │ ├── sphinxext.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── token.cpython-312.pyc
│ │ │ │ │ │ ├── unistring.cpython-312.pyc
│ │ │ │ │ │ └── util.cpython-312.pyc
│ │ │ │ │ ├── cmdline.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── filter.py
│ │ │ │ │ ├── filters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── formatter.py
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ ├── bbcode.cpython-312.pyc
│ │ │ │ │ │ │ ├── groff.cpython-312.pyc
│ │ │ │ │ │ │ ├── html.cpython-312.pyc
│ │ │ │ │ │ │ ├── img.cpython-312.pyc
│ │ │ │ │ │ │ ├── irc.cpython-312.pyc
│ │ │ │ │ │ │ ├── latex.cpython-312.pyc
│ │ │ │ │ │ │ ├── other.cpython-312.pyc
│ │ │ │ │ │ │ ├── pangomarkup.cpython-312.pyc
│ │ │ │ │ │ │ ├── rtf.cpython-312.pyc
│ │ │ │ │ │ │ ├── svg.cpython-312.pyc
│ │ │ │ │ │ │ ├── terminal.cpython-312.pyc
│ │ │ │ │ │ │ └── terminal256.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ ├── bbcode.py
│ │ │ │ │ │ ├── groff.py
│ │ │ │ │ │ ├── html.py
│ │ │ │ │ │ ├── img.py
│ │ │ │ │ │ ├── irc.py
│ │ │ │ │ │ ├── latex.py
│ │ │ │ │ │ ├── other.py
│ │ │ │ │ │ ├── pangomarkup.py
│ │ │ │ │ │ ├── rtf.py
│ │ │ │ │ │ ├── svg.py
│ │ │ │ │ │ ├── terminal.py
│ │ │ │ │ │ └── terminal256.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ ├── lexers
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ └── python.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ └── python.py
│ │ │ │ │ ├── modeline.py
│ │ │ │ │ ├── plugin.py
│ │ │ │ │ ├── regexopt.py
│ │ │ │ │ ├── scanner.py
│ │ │ │ │ ├── sphinxext.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styles
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── _mapping.cpython-312.pyc
│ │ │ │ │ │ └── _mapping.py
│ │ │ │ │ ├── token.py
│ │ │ │ │ ├── unistring.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── pyproject_hooks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ └── _impl.cpython-312.pyc
│ │ │ │ │ ├── _compat.py
│ │ │ │ │ ├── _impl.py
│ │ │ │ │ └── _in_process
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── _in_process.cpython-312.pyc
│ │ │ │ │ └── _in_process.py
│ │ │ │ ├── requests
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ │ ├── __version__.py
│ │ │ │ │ ├── _internal_utils.py
│ │ │ │ │ ├── adapters.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── certs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── cookies.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── hooks.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ ├── packages.py
│ │ │ │ │ ├── sessions.py
│ │ │ │ │ ├── status_codes.py
│ │ │ │ │ ├── structures.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── providers.cpython-312.pyc
│ │ │ │ │ │ ├── reporters.cpython-312.pyc
│ │ │ │ │ │ ├── resolvers.cpython-312.pyc
│ │ │ │ │ │ └── structs.cpython-312.pyc
│ │ │ │ │ ├── compat
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── collections_abc.cpython-312.pyc
│ │ │ │ │ │ └── collections_abc.py
│ │ │ │ │ ├── providers.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── reporters.py
│ │ │ │ │ ├── resolvers.py
│ │ │ │ │ └── structs.py
│ │ │ │ ├── rich
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── _cell_widths.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_codes.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_replace.cpython-312.pyc
│ │ │ │ │ │ ├── _export_format.cpython-312.pyc
│ │ │ │ │ │ ├── _extension.cpython-312.pyc
│ │ │ │ │ │ ├── _fileno.cpython-312.pyc
│ │ │ │ │ │ ├── _inspect.cpython-312.pyc
│ │ │ │ │ │ ├── _log_render.cpython-312.pyc
│ │ │ │ │ │ ├── _loop.cpython-312.pyc
│ │ │ │ │ │ ├── _null_file.cpython-312.pyc
│ │ │ │ │ │ ├── _palettes.cpython-312.pyc
│ │ │ │ │ │ ├── _pick.cpython-312.pyc
│ │ │ │ │ │ ├── _ratio.cpython-312.pyc
│ │ │ │ │ │ ├── _spinners.cpython-312.pyc
│ │ │ │ │ │ ├── _stack.cpython-312.pyc
│ │ │ │ │ │ ├── _timer.cpython-312.pyc
│ │ │ │ │ │ ├── _win32_console.cpython-312.pyc
│ │ │ │ │ │ ├── _windows_renderer.cpython-312.pyc
│ │ │ │ │ │ ├── _windows.cpython-312.pyc
│ │ │ │ │ │ ├── _wrap.cpython-312.pyc
│ │ │ │ │ │ ├── abc.cpython-312.pyc
│ │ │ │ │ │ ├── align.cpython-312.pyc
│ │ │ │ │ │ ├── ansi.cpython-312.pyc
│ │ │ │ │ │ ├── bar.cpython-312.pyc
│ │ │ │ │ │ ├── box.cpython-312.pyc
│ │ │ │ │ │ ├── cells.cpython-312.pyc
│ │ │ │ │ │ ├── color_triplet.cpython-312.pyc
│ │ │ │ │ │ ├── color.cpython-312.pyc
│ │ │ │ │ │ ├── columns.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── constrain.cpython-312.pyc
│ │ │ │ │ │ ├── containers.cpython-312.pyc
│ │ │ │ │ │ ├── control.cpython-312.pyc
│ │ │ │ │ │ ├── default_styles.cpython-312.pyc
│ │ │ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ │ │ ├── emoji.cpython-312.pyc
│ │ │ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ │ │ ├── file_proxy.cpython-312.pyc
│ │ │ │ │ │ ├── filesize.cpython-312.pyc
│ │ │ │ │ │ ├── highlighter.cpython-312.pyc
│ │ │ │ │ │ ├── json.cpython-312.pyc
│ │ │ │ │ │ ├── jupyter.cpython-312.pyc
│ │ │ │ │ │ ├── layout.cpython-312.pyc
│ │ │ │ │ │ ├── live_render.cpython-312.pyc
│ │ │ │ │ │ ├── live.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── markup.cpython-312.pyc
│ │ │ │ │ │ ├── measure.cpython-312.pyc
│ │ │ │ │ │ ├── padding.cpython-312.pyc
│ │ │ │ │ │ ├── pager.cpython-312.pyc
│ │ │ │ │ │ ├── palette.cpython-312.pyc
│ │ │ │ │ │ ├── panel.cpython-312.pyc
│ │ │ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bar.cpython-312.pyc
│ │ │ │ │ │ ├── progress.cpython-312.pyc
│ │ │ │ │ │ ├── prompt.cpython-312.pyc
│ │ │ │ │ │ ├── protocol.cpython-312.pyc
│ │ │ │ │ │ ├── region.cpython-312.pyc
│ │ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ │ ├── rule.cpython-312.pyc
│ │ │ │ │ │ ├── scope.cpython-312.pyc
│ │ │ │ │ │ ├── screen.cpython-312.pyc
│ │ │ │ │ │ ├── segment.cpython-312.pyc
│ │ │ │ │ │ ├── spinner.cpython-312.pyc
│ │ │ │ │ │ ├── status.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── styled.cpython-312.pyc
│ │ │ │ │ │ ├── syntax.cpython-312.pyc
│ │ │ │ │ │ ├── table.cpython-312.pyc
│ │ │ │ │ │ ├── terminal_theme.cpython-312.pyc
│ │ │ │ │ │ ├── text.cpython-312.pyc
│ │ │ │ │ │ ├── theme.cpython-312.pyc
│ │ │ │ │ │ ├── themes.cpython-312.pyc
│ │ │ │ │ │ ├── traceback.cpython-312.pyc
│ │ │ │ │ │ └── tree.cpython-312.pyc
│ │ │ │ │ ├── _cell_widths.py
│ │ │ │ │ ├── _emoji_codes.py
│ │ │ │ │ ├── _emoji_replace.py
│ │ │ │ │ ├── _export_format.py
│ │ │ │ │ ├── _extension.py
│ │ │ │ │ ├── _fileno.py
│ │ │ │ │ ├── _inspect.py
│ │ │ │ │ ├── _log_render.py
│ │ │ │ │ ├── _loop.py
│ │ │ │ │ ├── _null_file.py
│ │ │ │ │ ├── _palettes.py
│ │ │ │ │ ├── _pick.py
│ │ │ │ │ ├── _ratio.py
│ │ │ │ │ ├── _spinners.py
│ │ │ │ │ ├── _stack.py
│ │ │ │ │ ├── _timer.py
│ │ │ │ │ ├── _win32_console.py
│ │ │ │ │ ├── _windows_renderer.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ ├── _wrap.py
│ │ │ │ │ ├── abc.py
│ │ │ │ │ ├── align.py
│ │ │ │ │ ├── ansi.py
│ │ │ │ │ ├── bar.py
│ │ │ │ │ ├── box.py
│ │ │ │ │ ├── cells.py
│ │ │ │ │ ├── color_triplet.py
│ │ │ │ │ ├── color.py
│ │ │ │ │ ├── columns.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── constrain.py
│ │ │ │ │ ├── containers.py
│ │ │ │ │ ├── control.py
│ │ │ │ │ ├── default_styles.py
│ │ │ │ │ ├── diagnose.py
│ │ │ │ │ ├── emoji.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_proxy.py
│ │ │ │ │ ├── filesize.py
│ │ │ │ │ ├── highlighter.py
│ │ │ │ │ ├── json.py
│ │ │ │ │ ├── jupyter.py
│ │ │ │ │ ├── layout.py
│ │ │ │ │ ├── live_render.py
│ │ │ │ │ ├── live.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── markup.py
│ │ │ │ │ ├── measure.py
│ │ │ │ │ ├── padding.py
│ │ │ │ │ ├── pager.py
│ │ │ │ │ ├── palette.py
│ │ │ │ │ ├── panel.py
│ │ │ │ │ ├── pretty.py
│ │ │ │ │ ├── progress_bar.py
│ │ │ │ │ ├── progress.py
│ │ │ │ │ ├── prompt.py
│ │ │ │ │ ├── protocol.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── region.py
│ │ │ │ │ ├── repr.py
│ │ │ │ │ ├── rule.py
│ │ │ │ │ ├── scope.py
│ │ │ │ │ ├── screen.py
│ │ │ │ │ ├── segment.py
│ │ │ │ │ ├── spinner.py
│ │ │ │ │ ├── status.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styled.py
│ │ │ │ │ ├── syntax.py
│ │ │ │ │ ├── table.py
│ │ │ │ │ ├── terminal_theme.py
│ │ │ │ │ ├── text.py
│ │ │ │ │ ├── theme.py
│ │ │ │ │ ├── themes.py
│ │ │ │ │ ├── traceback.py
│ │ │ │ │ └── tree.py
│ │ │ │ ├── tomli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _re.cpython-312.pyc
│ │ │ │ │ │ └── _types.cpython-312.pyc
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _re.py
│ │ │ │ │ ├── _types.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── truststore
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _api.cpython-312.pyc
│ │ │ │ │ │ ├── _macos.cpython-312.pyc
│ │ │ │ │ │ ├── _openssl.cpython-312.pyc
│ │ │ │ │ │ ├── _ssl_constants.cpython-312.pyc
│ │ │ │ │ │ └── _windows.cpython-312.pyc
│ │ │ │ │ ├── _api.py
│ │ │ │ │ ├── _macos.py
│ │ │ │ │ ├── _openssl.py
│ │ │ │ │ ├── _ssl_constants.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── typing_extensions.py
│ │ │ │ ├── urllib3
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── _collections.py
│ │ │ │ │ ├── _version.py
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── connectionpool.py
│ │ │ │ │ ├── contrib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _appengine_environ.cpython-312.pyc
│ │ │ │ │ │ │ ├── appengine.cpython-312.pyc
│ │ │ │ │ │ │ ├── ntlmpool.cpython-312.pyc
│ │ │ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ │ │ ├── securetransport.cpython-312.pyc
│ │ │ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ │ │ ├── _appengine_environ.py
│ │ │ │ │ │ ├── _securetransport
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── bindings.cpython-312.pyc
│ │ │ │ │ │ │ │ └── low_level.cpython-312.pyc
│ │ │ │ │ │ │ ├── bindings.py
│ │ │ │ │ │ │ └── low_level.py
│ │ │ │ │ │ ├── appengine.py
│ │ │ │ │ │ ├── ntlmpool.py
│ │ │ │ │ │ ├── pyopenssl.py
│ │ │ │ │ │ ├── securetransport.py
│ │ │ │ │ │ └── socks.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── fields.py
│ │ │ │ │ ├── filepost.py
│ │ │ │ │ ├── packages
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── six.cpython-312.pyc
│ │ │ │ │ │ ├── backports
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── makefile.cpython-312.pyc
│ │ │ │ │ │ │ │ └── weakref_finalize.cpython-312.pyc
│ │ │ │ │ │ │ ├── makefile.py
│ │ │ │ │ │ │ └── weakref_finalize.py
│ │ │ │ │ │ └── six.py
│ │ │ │ │ ├── poolmanager.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ └── util
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ │ │ ├── queue.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ │ │ └── wait.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── proxy.py
│ │ │ │ │ ├── queue.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── ssl_.py
│ │ │ │ │ ├── ssl_match_hostname.py
│ │ │ │ │ ├── ssltransport.py
│ │ │ │ │ ├── timeout.py
│ │ │ │ │ ├── url.py
│ │ │ │ │ └── wait.py
│ │ │ │ └── vendor.txt
│ │ │ └── py.typed
│ │ ├── pip-24.2.dist-info
│ │ │ ├── AUTHORS.txt
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── requests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── __version__.py
│ │ │ ├── _internal_utils.py
│ │ │ ├── adapters.py
│ │ │ ├── api.py
│ │ │ ├── auth.py
│ │ │ ├── certs.py
│ │ │ ├── compat.py
│ │ │ ├── cookies.py
│ │ │ ├── exceptions.py
│ │ │ ├── help.py
│ │ │ ├── hooks.py
│ │ │ ├── models.py
│ │ │ ├── packages.py
│ │ │ ├── sessions.py
│ │ │ ├── status_codes.py
│ │ │ ├── structures.py
│ │ │ └── utils.py
│ │ ├── requests-2.32.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── soupsieve
│ │ │ ├── __init__.py
│ │ │ ├── __meta__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __meta__.cpython-312.pyc
│ │ │ │ ├── css_match.cpython-312.pyc
│ │ │ │ ├── css_parser.cpython-312.pyc
│ │ │ │ ├── css_types.cpython-312.pyc
│ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ └── util.cpython-312.pyc
│ │ │ ├── css_match.py
│ │ │ ├── css_parser.py
│ │ │ ├── css_types.py
│ │ │ ├── pretty.py
│ │ │ ├── py.typed
│ │ │ └── util.py
│ │ ├── soupsieve-2.6.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── urllib3
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _base_connection.cpython-312.pyc
│ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ ├── _request_methods.cpython-312.pyc
│ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ └── response.cpython-312.pyc
│ │ │ ├── _base_connection.py
│ │ │ ├── _collections.py
│ │ │ ├── _request_methods.py
│ │ │ ├── _version.py
│ │ │ ├── connection.py
│ │ │ ├── connectionpool.py
│ │ │ ├── contrib
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ ├── emscripten
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── fetch.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── emscripten_fetch_worker.js
│ │ │ │ │ ├── fetch.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ └── response.py
│ │ │ │ ├── pyopenssl.py
│ │ │ │ └── socks.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── filepost.py
│ │ │ ├── http2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ └── probe.cpython-312.pyc
│ │ │ │ ├── connection.py
│ │ │ │ └── probe.py
│ │ │ ├── poolmanager.py
│ │ │ ├── py.typed
│ │ │ ├── response.py
│ │ │ └── util
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ └── wait.cpython-312.pyc
│ │ │ ├── connection.py
│ │ │ ├── proxy.py
│ │ │ ├── request.py
│ │ │ ├── response.py
│ │ │ ├── retry.py
│ │ │ ├── ssl_.py
│ │ │ ├── ssl_match_hostname.py
│ │ │ ├── ssltransport.py
│ │ │ ├── timeout.py
│ │ │ ├── url.py
│ │ │ ├── util.py
│ │ │ └── wait.py
│ │ ├── urllib3-2.2.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── useragent
│ │ │ ├── __init__.py
│ │ │ ├── __init__.pyc
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── resources
│ │ │ │ └── user_agent_data.json
│ │ │ └── test
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── test_additional_os.json
│ │ │ ├── test_browser.json
│ │ │ ├── test_device.json
│ │ │ ├── test_firefox.json
│ │ │ ├── test_os.json
│ │ │ └── test_pgts_browser.json
│ │ ├── useragent-0.1.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── werkzeug
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _internal.cpython-312.pyc
│ │ │ │ ├── _reloader.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formparser.cpython-312.pyc
│ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ ├── local.cpython-312.pyc
│ │ │ │ ├── security.cpython-312.pyc
│ │ │ │ ├── serving.cpython-312.pyc
│ │ │ │ ├── test.cpython-312.pyc
│ │ │ │ ├── testapp.cpython-312.pyc
│ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ ├── user_agent.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── wsgi.cpython-312.pyc
│ │ │ ├── _internal.py
│ │ │ ├── _reloader.py
│ │ │ ├── datastructures
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── accept.cpython-312.pyc
│ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ ├── cache_control.cpython-312.pyc
│ │ │ │ │ ├── csp.cpython-312.pyc
│ │ │ │ │ ├── etag.cpython-312.pyc
│ │ │ │ │ ├── file_storage.cpython-312.pyc
│ │ │ │ │ ├── headers.cpython-312.pyc
│ │ │ │ │ ├── mixins.cpython-312.pyc
│ │ │ │ │ ├── range.cpython-312.pyc
│ │ │ │ │ └── structures.cpython-312.pyc
│ │ │ │ ├── accept.py
│ │ │ │ ├── accept.pyi
│ │ │ │ ├── auth.py
│ │ │ │ ├── cache_control.py
│ │ │ │ ├── cache_control.pyi
│ │ │ │ ├── csp.py
│ │ │ │ ├── csp.pyi
│ │ │ │ ├── etag.py
│ │ │ │ ├── etag.pyi
│ │ │ │ ├── file_storage.py
│ │ │ │ ├── file_storage.pyi
│ │ │ │ ├── headers.py
│ │ │ │ ├── headers.pyi
│ │ │ │ ├── mixins.py
│ │ │ │ ├── mixins.pyi
│ │ │ │ ├── range.py
│ │ │ │ ├── range.pyi
│ │ │ │ ├── structures.py
│ │ │ │ └── structures.pyi
│ │ │ ├── debug
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ └── tbtools.cpython-312.pyc
│ │ │ │ ├── console.py
│ │ │ │ ├── repr.py
│ │ │ │ ├── shared
│ │ │ │ │ ├── console.png
│ │ │ │ │ ├── debugger.js
│ │ │ │ │ ├── ICON_LICENSE.md
│ │ │ │ │ ├── less.png
│ │ │ │ │ ├── more.png
│ │ │ │ │ └── style.css
│ │ │ │ └── tbtools.py
│ │ │ ├── exceptions.py
│ │ │ ├── formparser.py
│ │ │ ├── http.py
│ │ │ ├── local.py
│ │ │ ├── middleware
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── dispatcher.cpython-312.pyc
│ │ │ │ │ ├── http_proxy.cpython-312.pyc
│ │ │ │ │ ├── lint.cpython-312.pyc
│ │ │ │ │ ├── profiler.cpython-312.pyc
│ │ │ │ │ ├── proxy_fix.cpython-312.pyc
│ │ │ │ │ └── shared_data.cpython-312.pyc
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── http_proxy.py
│ │ │ │ ├── lint.py
│ │ │ │ ├── profiler.py
│ │ │ │ ├── proxy_fix.py
│ │ │ │ └── shared_data.py
│ │ │ ├── py.typed
│ │ │ ├── routing
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── converters.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── map.cpython-312.pyc
│ │ │ │ │ ├── matcher.cpython-312.pyc
│ │ │ │ │ └── rules.cpython-312.pyc
│ │ │ │ ├── converters.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── map.py
│ │ │ │ ├── matcher.py
│ │ │ │ └── rules.py
│ │ │ ├── sansio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ │ ├── multipart.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ ├── http.py
│ │ │ │ ├── multipart.py
│ │ │ │ ├── request.py
│ │ │ │ ├── response.py
│ │ │ │ └── utils.py
│ │ │ ├── security.py
│ │ │ ├── serving.py
│ │ │ ├── test.py
│ │ │ ├── testapp.py
│ │ │ ├── urls.py
│ │ │ ├── user_agent.py
│ │ │ ├── utils.py
│ │ │ ├── wrappers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ ├── request.py
│ │ │ │ └── response.py
│ │ │ └── wsgi.py
│ │ └── werkzeug-3.0.4.dist-info
│ │ ├── INSTALLER
│ │ ├── LICENSE.txt
│ │ ├── METADATA
│ │ ├── RECORD
│ │ └── WHEEL
│ ├── pyvenv.cfg
│ ├── static
│ │ └── styles.css
│ ├── templates
│ │ └── index.html
│ └── test.py
├── cline_config.json
├── mcp_server.py
├── README.md
├── search_results.json
├── settings.json
└── test_files
├── text1.txt
└── text2.txt
```
# Files
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py:
--------------------------------------------------------------------------------
```python
1 | #
2 | # Copyright (C) 2012-2023 The Python Software Foundation.
3 | # See LICENSE.txt and CONTRIBUTORS.txt.
4 | #
5 | import codecs
6 | from collections import deque
7 | import contextlib
8 | import csv
9 | from glob import iglob as std_iglob
10 | import io
11 | import json
12 | import logging
13 | import os
14 | import py_compile
15 | import re
16 | import socket
17 | try:
18 | import ssl
19 | except ImportError: # pragma: no cover
20 | ssl = None
21 | import subprocess
22 | import sys
23 | import tarfile
24 | import tempfile
25 | import textwrap
26 |
27 | try:
28 | import threading
29 | except ImportError: # pragma: no cover
30 | import dummy_threading as threading
31 | import time
32 |
33 | from . import DistlibException
34 | from .compat import (string_types, text_type, shutil, raw_input, StringIO,
35 | cache_from_source, urlopen, urljoin, httplib, xmlrpclib,
36 | HTTPHandler, BaseConfigurator, valid_ident,
37 | Container, configparser, URLError, ZipFile, fsdecode,
38 | unquote, urlparse)
39 |
40 | logger = logging.getLogger(__name__)
41 |
42 | #
43 | # Requirement parsing code as per PEP 508
44 | #
45 |
46 | IDENTIFIER = re.compile(r'^([\w\.-]+)\s*')
47 | VERSION_IDENTIFIER = re.compile(r'^([\w\.*+-]+)\s*')
48 | COMPARE_OP = re.compile(r'^(<=?|>=?|={2,3}|[~!]=)\s*')
49 | MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*')
50 | OR = re.compile(r'^or\b\s*')
51 | AND = re.compile(r'^and\b\s*')
52 | NON_SPACE = re.compile(r'(\S+)\s*')
53 | STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)')
54 |
55 |
56 | def parse_marker(marker_string):
57 | """
58 | Parse a marker string and return a dictionary containing a marker expression.
59 |
60 | The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in
61 | the expression grammar, or strings. A string contained in quotes is to be
62 | interpreted as a literal string, and a string not contained in quotes is a
63 | variable (such as os_name).
64 | """
65 |
66 | def marker_var(remaining):
67 | # either identifier, or literal string
68 | m = IDENTIFIER.match(remaining)
69 | if m:
70 | result = m.groups()[0]
71 | remaining = remaining[m.end():]
72 | elif not remaining:
73 | raise SyntaxError('unexpected end of input')
74 | else:
75 | q = remaining[0]
76 | if q not in '\'"':
77 | raise SyntaxError('invalid expression: %s' % remaining)
78 | oq = '\'"'.replace(q, '')
79 | remaining = remaining[1:]
80 | parts = [q]
81 | while remaining:
82 | # either a string chunk, or oq, or q to terminate
83 | if remaining[0] == q:
84 | break
85 | elif remaining[0] == oq:
86 | parts.append(oq)
87 | remaining = remaining[1:]
88 | else:
89 | m = STRING_CHUNK.match(remaining)
90 | if not m:
91 | raise SyntaxError('error in string literal: %s' %
92 | remaining)
93 | parts.append(m.groups()[0])
94 | remaining = remaining[m.end():]
95 | else:
96 | s = ''.join(parts)
97 | raise SyntaxError('unterminated string: %s' % s)
98 | parts.append(q)
99 | result = ''.join(parts)
100 | remaining = remaining[1:].lstrip() # skip past closing quote
101 | return result, remaining
102 |
103 | def marker_expr(remaining):
104 | if remaining and remaining[0] == '(':
105 | result, remaining = marker(remaining[1:].lstrip())
106 | if remaining[0] != ')':
107 | raise SyntaxError('unterminated parenthesis: %s' % remaining)
108 | remaining = remaining[1:].lstrip()
109 | else:
110 | lhs, remaining = marker_var(remaining)
111 | while remaining:
112 | m = MARKER_OP.match(remaining)
113 | if not m:
114 | break
115 | op = m.groups()[0]
116 | remaining = remaining[m.end():]
117 | rhs, remaining = marker_var(remaining)
118 | lhs = {'op': op, 'lhs': lhs, 'rhs': rhs}
119 | result = lhs
120 | return result, remaining
121 |
122 | def marker_and(remaining):
123 | lhs, remaining = marker_expr(remaining)
124 | while remaining:
125 | m = AND.match(remaining)
126 | if not m:
127 | break
128 | remaining = remaining[m.end():]
129 | rhs, remaining = marker_expr(remaining)
130 | lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs}
131 | return lhs, remaining
132 |
133 | def marker(remaining):
134 | lhs, remaining = marker_and(remaining)
135 | while remaining:
136 | m = OR.match(remaining)
137 | if not m:
138 | break
139 | remaining = remaining[m.end():]
140 | rhs, remaining = marker_and(remaining)
141 | lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs}
142 | return lhs, remaining
143 |
144 | return marker(marker_string)
145 |
146 |
147 | def parse_requirement(req):
148 | """
149 | Parse a requirement passed in as a string. Return a Container
150 | whose attributes contain the various parts of the requirement.
151 | """
152 | remaining = req.strip()
153 | if not remaining or remaining.startswith('#'):
154 | return None
155 | m = IDENTIFIER.match(remaining)
156 | if not m:
157 | raise SyntaxError('name expected: %s' % remaining)
158 | distname = m.groups()[0]
159 | remaining = remaining[m.end():]
160 | extras = mark_expr = versions = uri = None
161 | if remaining and remaining[0] == '[':
162 | i = remaining.find(']', 1)
163 | if i < 0:
164 | raise SyntaxError('unterminated extra: %s' % remaining)
165 | s = remaining[1:i]
166 | remaining = remaining[i + 1:].lstrip()
167 | extras = []
168 | while s:
169 | m = IDENTIFIER.match(s)
170 | if not m:
171 | raise SyntaxError('malformed extra: %s' % s)
172 | extras.append(m.groups()[0])
173 | s = s[m.end():]
174 | if not s:
175 | break
176 | if s[0] != ',':
177 | raise SyntaxError('comma expected in extras: %s' % s)
178 | s = s[1:].lstrip()
179 | if not extras:
180 | extras = None
181 | if remaining:
182 | if remaining[0] == '@':
183 | # it's a URI
184 | remaining = remaining[1:].lstrip()
185 | m = NON_SPACE.match(remaining)
186 | if not m:
187 | raise SyntaxError('invalid URI: %s' % remaining)
188 | uri = m.groups()[0]
189 | t = urlparse(uri)
190 | # there are issues with Python and URL parsing, so this test
191 | # is a bit crude. See bpo-20271, bpo-23505. Python doesn't
192 | # always parse invalid URLs correctly - it should raise
193 | # exceptions for malformed URLs
194 | if not (t.scheme and t.netloc):
195 | raise SyntaxError('Invalid URL: %s' % uri)
196 | remaining = remaining[m.end():].lstrip()
197 | else:
198 |
199 | def get_versions(ver_remaining):
200 | """
201 | Return a list of operator, version tuples if any are
202 | specified, else None.
203 | """
204 | m = COMPARE_OP.match(ver_remaining)
205 | versions = None
206 | if m:
207 | versions = []
208 | while True:
209 | op = m.groups()[0]
210 | ver_remaining = ver_remaining[m.end():]
211 | m = VERSION_IDENTIFIER.match(ver_remaining)
212 | if not m:
213 | raise SyntaxError('invalid version: %s' %
214 | ver_remaining)
215 | v = m.groups()[0]
216 | versions.append((op, v))
217 | ver_remaining = ver_remaining[m.end():]
218 | if not ver_remaining or ver_remaining[0] != ',':
219 | break
220 | ver_remaining = ver_remaining[1:].lstrip()
221 | # Some packages have a trailing comma which would break things
222 | # See issue #148
223 | if not ver_remaining:
224 | break
225 | m = COMPARE_OP.match(ver_remaining)
226 | if not m:
227 | raise SyntaxError('invalid constraint: %s' %
228 | ver_remaining)
229 | if not versions:
230 | versions = None
231 | return versions, ver_remaining
232 |
233 | if remaining[0] != '(':
234 | versions, remaining = get_versions(remaining)
235 | else:
236 | i = remaining.find(')', 1)
237 | if i < 0:
238 | raise SyntaxError('unterminated parenthesis: %s' %
239 | remaining)
240 | s = remaining[1:i]
241 | remaining = remaining[i + 1:].lstrip()
242 | # As a special diversion from PEP 508, allow a version number
243 | # a.b.c in parentheses as a synonym for ~= a.b.c (because this
244 | # is allowed in earlier PEPs)
245 | if COMPARE_OP.match(s):
246 | versions, _ = get_versions(s)
247 | else:
248 | m = VERSION_IDENTIFIER.match(s)
249 | if not m:
250 | raise SyntaxError('invalid constraint: %s' % s)
251 | v = m.groups()[0]
252 | s = s[m.end():].lstrip()
253 | if s:
254 | raise SyntaxError('invalid constraint: %s' % s)
255 | versions = [('~=', v)]
256 |
257 | if remaining:
258 | if remaining[0] != ';':
259 | raise SyntaxError('invalid requirement: %s' % remaining)
260 | remaining = remaining[1:].lstrip()
261 |
262 | mark_expr, remaining = parse_marker(remaining)
263 |
264 | if remaining and remaining[0] != '#':
265 | raise SyntaxError('unexpected trailing data: %s' % remaining)
266 |
267 | if not versions:
268 | rs = distname
269 | else:
270 | rs = '%s %s' % (distname, ', '.join(
271 | ['%s %s' % con for con in versions]))
272 | return Container(name=distname,
273 | extras=extras,
274 | constraints=versions,
275 | marker=mark_expr,
276 | url=uri,
277 | requirement=rs)
278 |
279 |
280 | def get_resources_dests(resources_root, rules):
281 | """Find destinations for resources files"""
282 |
283 | def get_rel_path(root, path):
284 | # normalizes and returns a lstripped-/-separated path
285 | root = root.replace(os.path.sep, '/')
286 | path = path.replace(os.path.sep, '/')
287 | assert path.startswith(root)
288 | return path[len(root):].lstrip('/')
289 |
290 | destinations = {}
291 | for base, suffix, dest in rules:
292 | prefix = os.path.join(resources_root, base)
293 | for abs_base in iglob(prefix):
294 | abs_glob = os.path.join(abs_base, suffix)
295 | for abs_path in iglob(abs_glob):
296 | resource_file = get_rel_path(resources_root, abs_path)
297 | if dest is None: # remove the entry if it was here
298 | destinations.pop(resource_file, None)
299 | else:
300 | rel_path = get_rel_path(abs_base, abs_path)
301 | rel_dest = dest.replace(os.path.sep, '/').rstrip('/')
302 | destinations[resource_file] = rel_dest + '/' + rel_path
303 | return destinations
304 |
305 |
306 | def in_venv():
307 | if hasattr(sys, 'real_prefix'):
308 | # virtualenv venvs
309 | result = True
310 | else:
311 | # PEP 405 venvs
312 | result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix)
313 | return result
314 |
315 |
316 | def get_executable():
317 | # The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as
318 | # changes to the stub launcher mean that sys.executable always points
319 | # to the stub on OS X
320 | # if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
321 | # in os.environ):
322 | # result = os.environ['__PYVENV_LAUNCHER__']
323 | # else:
324 | # result = sys.executable
325 | # return result
326 | # Avoid normcasing: see issue #143
327 | # result = os.path.normcase(sys.executable)
328 | result = sys.executable
329 | if not isinstance(result, text_type):
330 | result = fsdecode(result)
331 | return result
332 |
333 |
334 | def proceed(prompt, allowed_chars, error_prompt=None, default=None):
335 | p = prompt
336 | while True:
337 | s = raw_input(p)
338 | p = prompt
339 | if not s and default:
340 | s = default
341 | if s:
342 | c = s[0].lower()
343 | if c in allowed_chars:
344 | break
345 | if error_prompt:
346 | p = '%c: %s\n%s' % (c, error_prompt, prompt)
347 | return c
348 |
349 |
350 | def extract_by_key(d, keys):
351 | if isinstance(keys, string_types):
352 | keys = keys.split()
353 | result = {}
354 | for key in keys:
355 | if key in d:
356 | result[key] = d[key]
357 | return result
358 |
359 |
360 | def read_exports(stream):
361 | if sys.version_info[0] >= 3:
362 | # needs to be a text stream
363 | stream = codecs.getreader('utf-8')(stream)
364 | # Try to load as JSON, falling back on legacy format
365 | data = stream.read()
366 | stream = StringIO(data)
367 | try:
368 | jdata = json.load(stream)
369 | result = jdata['extensions']['python.exports']['exports']
370 | for group, entries in result.items():
371 | for k, v in entries.items():
372 | s = '%s = %s' % (k, v)
373 | entry = get_export_entry(s)
374 | assert entry is not None
375 | entries[k] = entry
376 | return result
377 | except Exception:
378 | stream.seek(0, 0)
379 |
380 | def read_stream(cp, stream):
381 | if hasattr(cp, 'read_file'):
382 | cp.read_file(stream)
383 | else:
384 | cp.readfp(stream)
385 |
386 | cp = configparser.ConfigParser()
387 | try:
388 | read_stream(cp, stream)
389 | except configparser.MissingSectionHeaderError:
390 | stream.close()
391 | data = textwrap.dedent(data)
392 | stream = StringIO(data)
393 | read_stream(cp, stream)
394 |
395 | result = {}
396 | for key in cp.sections():
397 | result[key] = entries = {}
398 | for name, value in cp.items(key):
399 | s = '%s = %s' % (name, value)
400 | entry = get_export_entry(s)
401 | assert entry is not None
402 | # entry.dist = self
403 | entries[name] = entry
404 | return result
405 |
406 |
407 | def write_exports(exports, stream):
408 | if sys.version_info[0] >= 3:
409 | # needs to be a text stream
410 | stream = codecs.getwriter('utf-8')(stream)
411 | cp = configparser.ConfigParser()
412 | for k, v in exports.items():
413 | # TODO check k, v for valid values
414 | cp.add_section(k)
415 | for entry in v.values():
416 | if entry.suffix is None:
417 | s = entry.prefix
418 | else:
419 | s = '%s:%s' % (entry.prefix, entry.suffix)
420 | if entry.flags:
421 | s = '%s [%s]' % (s, ', '.join(entry.flags))
422 | cp.set(k, entry.name, s)
423 | cp.write(stream)
424 |
425 |
426 | @contextlib.contextmanager
427 | def tempdir():
428 | td = tempfile.mkdtemp()
429 | try:
430 | yield td
431 | finally:
432 | shutil.rmtree(td)
433 |
434 |
435 | @contextlib.contextmanager
436 | def chdir(d):
437 | cwd = os.getcwd()
438 | try:
439 | os.chdir(d)
440 | yield
441 | finally:
442 | os.chdir(cwd)
443 |
444 |
445 | @contextlib.contextmanager
446 | def socket_timeout(seconds=15):
447 | cto = socket.getdefaulttimeout()
448 | try:
449 | socket.setdefaulttimeout(seconds)
450 | yield
451 | finally:
452 | socket.setdefaulttimeout(cto)
453 |
454 |
455 | class cached_property(object):
456 |
457 | def __init__(self, func):
458 | self.func = func
459 | # for attr in ('__name__', '__module__', '__doc__'):
460 | # setattr(self, attr, getattr(func, attr, None))
461 |
462 | def __get__(self, obj, cls=None):
463 | if obj is None:
464 | return self
465 | value = self.func(obj)
466 | object.__setattr__(obj, self.func.__name__, value)
467 | # obj.__dict__[self.func.__name__] = value = self.func(obj)
468 | return value
469 |
470 |
471 | def convert_path(pathname):
472 | """Return 'pathname' as a name that will work on the native filesystem.
473 |
474 | The path is split on '/' and put back together again using the current
475 | directory separator. Needed because filenames in the setup script are
476 | always supplied in Unix style, and have to be converted to the local
477 | convention before we can actually use them in the filesystem. Raises
478 | ValueError on non-Unix-ish systems if 'pathname' either starts or
479 | ends with a slash.
480 | """
481 | if os.sep == '/':
482 | return pathname
483 | if not pathname:
484 | return pathname
485 | if pathname[0] == '/':
486 | raise ValueError("path '%s' cannot be absolute" % pathname)
487 | if pathname[-1] == '/':
488 | raise ValueError("path '%s' cannot end with '/'" % pathname)
489 |
490 | paths = pathname.split('/')
491 | while os.curdir in paths:
492 | paths.remove(os.curdir)
493 | if not paths:
494 | return os.curdir
495 | return os.path.join(*paths)
496 |
497 |
498 | class FileOperator(object):
499 |
500 | def __init__(self, dry_run=False):
501 | self.dry_run = dry_run
502 | self.ensured = set()
503 | self._init_record()
504 |
505 | def _init_record(self):
506 | self.record = False
507 | self.files_written = set()
508 | self.dirs_created = set()
509 |
510 | def record_as_written(self, path):
511 | if self.record:
512 | self.files_written.add(path)
513 |
514 | def newer(self, source, target):
515 | """Tell if the target is newer than the source.
516 |
517 | Returns true if 'source' exists and is more recently modified than
518 | 'target', or if 'source' exists and 'target' doesn't.
519 |
520 | Returns false if both exist and 'target' is the same age or younger
521 | than 'source'. Raise PackagingFileError if 'source' does not exist.
522 |
523 | Note that this test is not very accurate: files created in the same
524 | second will have the same "age".
525 | """
526 | if not os.path.exists(source):
527 | raise DistlibException("file '%r' does not exist" %
528 | os.path.abspath(source))
529 | if not os.path.exists(target):
530 | return True
531 |
532 | return os.stat(source).st_mtime > os.stat(target).st_mtime
533 |
534 | def copy_file(self, infile, outfile, check=True):
535 | """Copy a file respecting dry-run and force flags.
536 | """
537 | self.ensure_dir(os.path.dirname(outfile))
538 | logger.info('Copying %s to %s', infile, outfile)
539 | if not self.dry_run:
540 | msg = None
541 | if check:
542 | if os.path.islink(outfile):
543 | msg = '%s is a symlink' % outfile
544 | elif os.path.exists(outfile) and not os.path.isfile(outfile):
545 | msg = '%s is a non-regular file' % outfile
546 | if msg:
547 | raise ValueError(msg + ' which would be overwritten')
548 | shutil.copyfile(infile, outfile)
549 | self.record_as_written(outfile)
550 |
551 | def copy_stream(self, instream, outfile, encoding=None):
552 | assert not os.path.isdir(outfile)
553 | self.ensure_dir(os.path.dirname(outfile))
554 | logger.info('Copying stream %s to %s', instream, outfile)
555 | if not self.dry_run:
556 | if encoding is None:
557 | outstream = open(outfile, 'wb')
558 | else:
559 | outstream = codecs.open(outfile, 'w', encoding=encoding)
560 | try:
561 | shutil.copyfileobj(instream, outstream)
562 | finally:
563 | outstream.close()
564 | self.record_as_written(outfile)
565 |
566 | def write_binary_file(self, path, data):
567 | self.ensure_dir(os.path.dirname(path))
568 | if not self.dry_run:
569 | if os.path.exists(path):
570 | os.remove(path)
571 | with open(path, 'wb') as f:
572 | f.write(data)
573 | self.record_as_written(path)
574 |
575 | def write_text_file(self, path, data, encoding):
576 | self.write_binary_file(path, data.encode(encoding))
577 |
578 | def set_mode(self, bits, mask, files):
579 | if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'):
580 | # Set the executable bits (owner, group, and world) on
581 | # all the files specified.
582 | for f in files:
583 | if self.dry_run:
584 | logger.info("changing mode of %s", f)
585 | else:
586 | mode = (os.stat(f).st_mode | bits) & mask
587 | logger.info("changing mode of %s to %o", f, mode)
588 | os.chmod(f, mode)
589 |
590 | set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f)
591 |
592 | def ensure_dir(self, path):
593 | path = os.path.abspath(path)
594 | if path not in self.ensured and not os.path.exists(path):
595 | self.ensured.add(path)
596 | d, f = os.path.split(path)
597 | self.ensure_dir(d)
598 | logger.info('Creating %s' % path)
599 | if not self.dry_run:
600 | os.mkdir(path)
601 | if self.record:
602 | self.dirs_created.add(path)
603 |
604 | def byte_compile(self,
605 | path,
606 | optimize=False,
607 | force=False,
608 | prefix=None,
609 | hashed_invalidation=False):
610 | dpath = cache_from_source(path, not optimize)
611 | logger.info('Byte-compiling %s to %s', path, dpath)
612 | if not self.dry_run:
613 | if force or self.newer(path, dpath):
614 | if not prefix:
615 | diagpath = None
616 | else:
617 | assert path.startswith(prefix)
618 | diagpath = path[len(prefix):]
619 | compile_kwargs = {}
620 | if hashed_invalidation and hasattr(py_compile,
621 | 'PycInvalidationMode'):
622 | compile_kwargs[
623 | 'invalidation_mode'] = py_compile.PycInvalidationMode.CHECKED_HASH
624 | py_compile.compile(path, dpath, diagpath, True,
625 | **compile_kwargs) # raise error
626 | self.record_as_written(dpath)
627 | return dpath
628 |
629 | def ensure_removed(self, path):
630 | if os.path.exists(path):
631 | if os.path.isdir(path) and not os.path.islink(path):
632 | logger.debug('Removing directory tree at %s', path)
633 | if not self.dry_run:
634 | shutil.rmtree(path)
635 | if self.record:
636 | if path in self.dirs_created:
637 | self.dirs_created.remove(path)
638 | else:
639 | if os.path.islink(path):
640 | s = 'link'
641 | else:
642 | s = 'file'
643 | logger.debug('Removing %s %s', s, path)
644 | if not self.dry_run:
645 | os.remove(path)
646 | if self.record:
647 | if path in self.files_written:
648 | self.files_written.remove(path)
649 |
650 | def is_writable(self, path):
651 | result = False
652 | while not result:
653 | if os.path.exists(path):
654 | result = os.access(path, os.W_OK)
655 | break
656 | parent = os.path.dirname(path)
657 | if parent == path:
658 | break
659 | path = parent
660 | return result
661 |
662 | def commit(self):
663 | """
664 | Commit recorded changes, turn off recording, return
665 | changes.
666 | """
667 | assert self.record
668 | result = self.files_written, self.dirs_created
669 | self._init_record()
670 | return result
671 |
672 | def rollback(self):
673 | if not self.dry_run:
674 | for f in list(self.files_written):
675 | if os.path.exists(f):
676 | os.remove(f)
677 | # dirs should all be empty now, except perhaps for
678 | # __pycache__ subdirs
679 | # reverse so that subdirs appear before their parents
680 | dirs = sorted(self.dirs_created, reverse=True)
681 | for d in dirs:
682 | flist = os.listdir(d)
683 | if flist:
684 | assert flist == ['__pycache__']
685 | sd = os.path.join(d, flist[0])
686 | os.rmdir(sd)
687 | os.rmdir(d) # should fail if non-empty
688 | self._init_record()
689 |
690 |
691 | def resolve(module_name, dotted_path):
692 | if module_name in sys.modules:
693 | mod = sys.modules[module_name]
694 | else:
695 | mod = __import__(module_name)
696 | if dotted_path is None:
697 | result = mod
698 | else:
699 | parts = dotted_path.split('.')
700 | result = getattr(mod, parts.pop(0))
701 | for p in parts:
702 | result = getattr(result, p)
703 | return result
704 |
705 |
706 | class ExportEntry(object):
707 |
708 | def __init__(self, name, prefix, suffix, flags):
709 | self.name = name
710 | self.prefix = prefix
711 | self.suffix = suffix
712 | self.flags = flags
713 |
714 | @cached_property
715 | def value(self):
716 | return resolve(self.prefix, self.suffix)
717 |
718 | def __repr__(self): # pragma: no cover
719 | return '<ExportEntry %s = %s:%s %s>' % (self.name, self.prefix,
720 | self.suffix, self.flags)
721 |
722 | def __eq__(self, other):
723 | if not isinstance(other, ExportEntry):
724 | result = False
725 | else:
726 | result = (self.name == other.name and self.prefix == other.prefix
727 | and self.suffix == other.suffix
728 | and self.flags == other.flags)
729 | return result
730 |
731 | __hash__ = object.__hash__
732 |
733 |
734 | ENTRY_RE = re.compile(
735 | r'''(?P<name>([^\[]\S*))
736 | \s*=\s*(?P<callable>(\w+)([:\.]\w+)*)
737 | \s*(\[\s*(?P<flags>[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])?
738 | ''', re.VERBOSE)
739 |
740 |
741 | def get_export_entry(specification):
742 | m = ENTRY_RE.search(specification)
743 | if not m:
744 | result = None
745 | if '[' in specification or ']' in specification:
746 | raise DistlibException("Invalid specification "
747 | "'%s'" % specification)
748 | else:
749 | d = m.groupdict()
750 | name = d['name']
751 | path = d['callable']
752 | colons = path.count(':')
753 | if colons == 0:
754 | prefix, suffix = path, None
755 | else:
756 | if colons != 1:
757 | raise DistlibException("Invalid specification "
758 | "'%s'" % specification)
759 | prefix, suffix = path.split(':')
760 | flags = d['flags']
761 | if flags is None:
762 | if '[' in specification or ']' in specification:
763 | raise DistlibException("Invalid specification "
764 | "'%s'" % specification)
765 | flags = []
766 | else:
767 | flags = [f.strip() for f in flags.split(',')]
768 | result = ExportEntry(name, prefix, suffix, flags)
769 | return result
770 |
771 |
772 | def get_cache_base(suffix=None):
773 | """
774 | Return the default base location for distlib caches. If the directory does
775 | not exist, it is created. Use the suffix provided for the base directory,
776 | and default to '.distlib' if it isn't provided.
777 |
778 | On Windows, if LOCALAPPDATA is defined in the environment, then it is
779 | assumed to be a directory, and will be the parent directory of the result.
780 | On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home
781 | directory - using os.expanduser('~') - will be the parent directory of
782 | the result.
783 |
784 | The result is just the directory '.distlib' in the parent directory as
785 | determined above, or with the name specified with ``suffix``.
786 | """
787 | if suffix is None:
788 | suffix = '.distlib'
789 | if os.name == 'nt' and 'LOCALAPPDATA' in os.environ:
790 | result = os.path.expandvars('$localappdata')
791 | else:
792 | # Assume posix, or old Windows
793 | result = os.path.expanduser('~')
794 | # we use 'isdir' instead of 'exists', because we want to
795 | # fail if there's a file with that name
796 | if os.path.isdir(result):
797 | usable = os.access(result, os.W_OK)
798 | if not usable:
799 | logger.warning('Directory exists but is not writable: %s', result)
800 | else:
801 | try:
802 | os.makedirs(result)
803 | usable = True
804 | except OSError:
805 | logger.warning('Unable to create %s', result, exc_info=True)
806 | usable = False
807 | if not usable:
808 | result = tempfile.mkdtemp()
809 | logger.warning('Default location unusable, using %s', result)
810 | return os.path.join(result, suffix)
811 |
812 |
813 | def path_to_cache_dir(path):
814 | """
815 | Convert an absolute path to a directory name for use in a cache.
816 |
817 | The algorithm used is:
818 |
819 | #. On Windows, any ``':'`` in the drive is replaced with ``'---'``.
820 | #. Any occurrence of ``os.sep`` is replaced with ``'--'``.
821 | #. ``'.cache'`` is appended.
822 | """
823 | d, p = os.path.splitdrive(os.path.abspath(path))
824 | if d:
825 | d = d.replace(':', '---')
826 | p = p.replace(os.sep, '--')
827 | return d + p + '.cache'
828 |
829 |
830 | def ensure_slash(s):
831 | if not s.endswith('/'):
832 | return s + '/'
833 | return s
834 |
835 |
836 | def parse_credentials(netloc):
837 | username = password = None
838 | if '@' in netloc:
839 | prefix, netloc = netloc.rsplit('@', 1)
840 | if ':' not in prefix:
841 | username = prefix
842 | else:
843 | username, password = prefix.split(':', 1)
844 | if username:
845 | username = unquote(username)
846 | if password:
847 | password = unquote(password)
848 | return username, password, netloc
849 |
850 |
851 | def get_process_umask():
852 | result = os.umask(0o22)
853 | os.umask(result)
854 | return result
855 |
856 |
857 | def is_string_sequence(seq):
858 | result = True
859 | i = None
860 | for i, s in enumerate(seq):
861 | if not isinstance(s, string_types):
862 | result = False
863 | break
864 | assert i is not None
865 | return result
866 |
867 |
868 | PROJECT_NAME_AND_VERSION = re.compile(
869 | '([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-'
870 | '([a-z0-9_.+-]+)', re.I)
871 | PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)')
872 |
873 |
874 | def split_filename(filename, project_name=None):
875 | """
876 | Extract name, version, python version from a filename (no extension)
877 |
878 | Return name, version, pyver or None
879 | """
880 | result = None
881 | pyver = None
882 | filename = unquote(filename).replace(' ', '-')
883 | m = PYTHON_VERSION.search(filename)
884 | if m:
885 | pyver = m.group(1)
886 | filename = filename[:m.start()]
887 | if project_name and len(filename) > len(project_name) + 1:
888 | m = re.match(re.escape(project_name) + r'\b', filename)
889 | if m:
890 | n = m.end()
891 | result = filename[:n], filename[n + 1:], pyver
892 | if result is None:
893 | m = PROJECT_NAME_AND_VERSION.match(filename)
894 | if m:
895 | result = m.group(1), m.group(3), pyver
896 | return result
897 |
898 |
899 | # Allow spaces in name because of legacy dists like "Twisted Core"
900 | NAME_VERSION_RE = re.compile(r'(?P<name>[\w .-]+)\s*'
901 | r'\(\s*(?P<ver>[^\s)]+)\)$')
902 |
903 |
904 | def parse_name_and_version(p):
905 | """
906 | A utility method used to get name and version from a string.
907 |
908 | From e.g. a Provides-Dist value.
909 |
910 | :param p: A value in a form 'foo (1.0)'
911 | :return: The name and version as a tuple.
912 | """
913 | m = NAME_VERSION_RE.match(p)
914 | if not m:
915 | raise DistlibException('Ill-formed name/version string: \'%s\'' % p)
916 | d = m.groupdict()
917 | return d['name'].strip().lower(), d['ver']
918 |
919 |
920 | def get_extras(requested, available):
921 | result = set()
922 | requested = set(requested or [])
923 | available = set(available or [])
924 | if '*' in requested:
925 | requested.remove('*')
926 | result |= available
927 | for r in requested:
928 | if r == '-':
929 | result.add(r)
930 | elif r.startswith('-'):
931 | unwanted = r[1:]
932 | if unwanted not in available:
933 | logger.warning('undeclared extra: %s' % unwanted)
934 | if unwanted in result:
935 | result.remove(unwanted)
936 | else:
937 | if r not in available:
938 | logger.warning('undeclared extra: %s' % r)
939 | result.add(r)
940 | return result
941 |
942 |
943 | #
944 | # Extended metadata functionality
945 | #
946 |
947 |
948 | def _get_external_data(url):
949 | result = {}
950 | try:
951 | # urlopen might fail if it runs into redirections,
952 | # because of Python issue #13696. Fixed in locators
953 | # using a custom redirect handler.
954 | resp = urlopen(url)
955 | headers = resp.info()
956 | ct = headers.get('Content-Type')
957 | if not ct.startswith('application/json'):
958 | logger.debug('Unexpected response for JSON request: %s', ct)
959 | else:
960 | reader = codecs.getreader('utf-8')(resp)
961 | # data = reader.read().decode('utf-8')
962 | # result = json.loads(data)
963 | result = json.load(reader)
964 | except Exception as e:
965 | logger.exception('Failed to get external data for %s: %s', url, e)
966 | return result
967 |
968 |
969 | _external_data_base_url = 'https://www.red-dove.com/pypi/projects/'
970 |
971 |
972 | def get_project_data(name):
973 | url = '%s/%s/project.json' % (name[0].upper(), name)
974 | url = urljoin(_external_data_base_url, url)
975 | result = _get_external_data(url)
976 | return result
977 |
978 |
979 | def get_package_data(name, version):
980 | url = '%s/%s/package-%s.json' % (name[0].upper(), name, version)
981 | url = urljoin(_external_data_base_url, url)
982 | return _get_external_data(url)
983 |
984 |
985 | class Cache(object):
986 | """
987 | A class implementing a cache for resources that need to live in the file system
988 | e.g. shared libraries. This class was moved from resources to here because it
989 | could be used by other modules, e.g. the wheel module.
990 | """
991 |
992 | def __init__(self, base):
993 | """
994 | Initialise an instance.
995 |
996 | :param base: The base directory where the cache should be located.
997 | """
998 | # we use 'isdir' instead of 'exists', because we want to
999 | # fail if there's a file with that name
1000 | if not os.path.isdir(base): # pragma: no cover
1001 | os.makedirs(base)
1002 | if (os.stat(base).st_mode & 0o77) != 0:
1003 | logger.warning('Directory \'%s\' is not private', base)
1004 | self.base = os.path.abspath(os.path.normpath(base))
1005 |
1006 | def prefix_to_dir(self, prefix):
1007 | """
1008 | Converts a resource prefix to a directory name in the cache.
1009 | """
1010 | return path_to_cache_dir(prefix)
1011 |
1012 | def clear(self):
1013 | """
1014 | Clear the cache.
1015 | """
1016 | not_removed = []
1017 | for fn in os.listdir(self.base):
1018 | fn = os.path.join(self.base, fn)
1019 | try:
1020 | if os.path.islink(fn) or os.path.isfile(fn):
1021 | os.remove(fn)
1022 | elif os.path.isdir(fn):
1023 | shutil.rmtree(fn)
1024 | except Exception:
1025 | not_removed.append(fn)
1026 | return not_removed
1027 |
1028 |
1029 | class EventMixin(object):
1030 | """
1031 | A very simple publish/subscribe system.
1032 | """
1033 |
1034 | def __init__(self):
1035 | self._subscribers = {}
1036 |
1037 | def add(self, event, subscriber, append=True):
1038 | """
1039 | Add a subscriber for an event.
1040 |
1041 | :param event: The name of an event.
1042 | :param subscriber: The subscriber to be added (and called when the
1043 | event is published).
1044 | :param append: Whether to append or prepend the subscriber to an
1045 | existing subscriber list for the event.
1046 | """
1047 | subs = self._subscribers
1048 | if event not in subs:
1049 | subs[event] = deque([subscriber])
1050 | else:
1051 | sq = subs[event]
1052 | if append:
1053 | sq.append(subscriber)
1054 | else:
1055 | sq.appendleft(subscriber)
1056 |
1057 | def remove(self, event, subscriber):
1058 | """
1059 | Remove a subscriber for an event.
1060 |
1061 | :param event: The name of an event.
1062 | :param subscriber: The subscriber to be removed.
1063 | """
1064 | subs = self._subscribers
1065 | if event not in subs:
1066 | raise ValueError('No subscribers: %r' % event)
1067 | subs[event].remove(subscriber)
1068 |
1069 | def get_subscribers(self, event):
1070 | """
1071 | Return an iterator for the subscribers for an event.
1072 | :param event: The event to return subscribers for.
1073 | """
1074 | return iter(self._subscribers.get(event, ()))
1075 |
1076 | def publish(self, event, *args, **kwargs):
1077 | """
1078 | Publish a event and return a list of values returned by its
1079 | subscribers.
1080 |
1081 | :param event: The event to publish.
1082 | :param args: The positional arguments to pass to the event's
1083 | subscribers.
1084 | :param kwargs: The keyword arguments to pass to the event's
1085 | subscribers.
1086 | """
1087 | result = []
1088 | for subscriber in self.get_subscribers(event):
1089 | try:
1090 | value = subscriber(event, *args, **kwargs)
1091 | except Exception:
1092 | logger.exception('Exception during event publication')
1093 | value = None
1094 | result.append(value)
1095 | logger.debug('publish %s: args = %s, kwargs = %s, result = %s', event,
1096 | args, kwargs, result)
1097 | return result
1098 |
1099 |
1100 | #
1101 | # Simple sequencing
1102 | #
1103 | class Sequencer(object):
1104 |
1105 | def __init__(self):
1106 | self._preds = {}
1107 | self._succs = {}
1108 | self._nodes = set() # nodes with no preds/succs
1109 |
1110 | def add_node(self, node):
1111 | self._nodes.add(node)
1112 |
1113 | def remove_node(self, node, edges=False):
1114 | if node in self._nodes:
1115 | self._nodes.remove(node)
1116 | if edges:
1117 | for p in set(self._preds.get(node, ())):
1118 | self.remove(p, node)
1119 | for s in set(self._succs.get(node, ())):
1120 | self.remove(node, s)
1121 | # Remove empties
1122 | for k, v in list(self._preds.items()):
1123 | if not v:
1124 | del self._preds[k]
1125 | for k, v in list(self._succs.items()):
1126 | if not v:
1127 | del self._succs[k]
1128 |
1129 | def add(self, pred, succ):
1130 | assert pred != succ
1131 | self._preds.setdefault(succ, set()).add(pred)
1132 | self._succs.setdefault(pred, set()).add(succ)
1133 |
1134 | def remove(self, pred, succ):
1135 | assert pred != succ
1136 | try:
1137 | preds = self._preds[succ]
1138 | succs = self._succs[pred]
1139 | except KeyError: # pragma: no cover
1140 | raise ValueError('%r not a successor of anything' % succ)
1141 | try:
1142 | preds.remove(pred)
1143 | succs.remove(succ)
1144 | except KeyError: # pragma: no cover
1145 | raise ValueError('%r not a successor of %r' % (succ, pred))
1146 |
1147 | def is_step(self, step):
1148 | return (step in self._preds or step in self._succs
1149 | or step in self._nodes)
1150 |
1151 | def get_steps(self, final):
1152 | if not self.is_step(final):
1153 | raise ValueError('Unknown: %r' % final)
1154 | result = []
1155 | todo = []
1156 | seen = set()
1157 | todo.append(final)
1158 | while todo:
1159 | step = todo.pop(0)
1160 | if step in seen:
1161 | # if a step was already seen,
1162 | # move it to the end (so it will appear earlier
1163 | # when reversed on return) ... but not for the
1164 | # final step, as that would be confusing for
1165 | # users
1166 | if step != final:
1167 | result.remove(step)
1168 | result.append(step)
1169 | else:
1170 | seen.add(step)
1171 | result.append(step)
1172 | preds = self._preds.get(step, ())
1173 | todo.extend(preds)
1174 | return reversed(result)
1175 |
1176 | @property
1177 | def strong_connections(self):
1178 | # http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
1179 | index_counter = [0]
1180 | stack = []
1181 | lowlinks = {}
1182 | index = {}
1183 | result = []
1184 |
1185 | graph = self._succs
1186 |
1187 | def strongconnect(node):
1188 | # set the depth index for this node to the smallest unused index
1189 | index[node] = index_counter[0]
1190 | lowlinks[node] = index_counter[0]
1191 | index_counter[0] += 1
1192 | stack.append(node)
1193 |
1194 | # Consider successors
1195 | try:
1196 | successors = graph[node]
1197 | except Exception:
1198 | successors = []
1199 | for successor in successors:
1200 | if successor not in lowlinks:
1201 | # Successor has not yet been visited
1202 | strongconnect(successor)
1203 | lowlinks[node] = min(lowlinks[node], lowlinks[successor])
1204 | elif successor in stack:
1205 | # the successor is in the stack and hence in the current
1206 | # strongly connected component (SCC)
1207 | lowlinks[node] = min(lowlinks[node], index[successor])
1208 |
1209 | # If `node` is a root node, pop the stack and generate an SCC
1210 | if lowlinks[node] == index[node]:
1211 | connected_component = []
1212 |
1213 | while True:
1214 | successor = stack.pop()
1215 | connected_component.append(successor)
1216 | if successor == node:
1217 | break
1218 | component = tuple(connected_component)
1219 | # storing the result
1220 | result.append(component)
1221 |
1222 | for node in graph:
1223 | if node not in lowlinks:
1224 | strongconnect(node)
1225 |
1226 | return result
1227 |
1228 | @property
1229 | def dot(self):
1230 | result = ['digraph G {']
1231 | for succ in self._preds:
1232 | preds = self._preds[succ]
1233 | for pred in preds:
1234 | result.append(' %s -> %s;' % (pred, succ))
1235 | for node in self._nodes:
1236 | result.append(' %s;' % node)
1237 | result.append('}')
1238 | return '\n'.join(result)
1239 |
1240 |
1241 | #
1242 | # Unarchiving functionality for zip, tar, tgz, tbz, whl
1243 | #
1244 |
1245 | ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz',
1246 | '.whl')
1247 |
1248 |
1249 | def unarchive(archive_filename, dest_dir, format=None, check=True):
1250 |
1251 | def check_path(path):
1252 | if not isinstance(path, text_type):
1253 | path = path.decode('utf-8')
1254 | p = os.path.abspath(os.path.join(dest_dir, path))
1255 | if not p.startswith(dest_dir) or p[plen] != os.sep:
1256 | raise ValueError('path outside destination: %r' % p)
1257 |
1258 | dest_dir = os.path.abspath(dest_dir)
1259 | plen = len(dest_dir)
1260 | archive = None
1261 | if format is None:
1262 | if archive_filename.endswith(('.zip', '.whl')):
1263 | format = 'zip'
1264 | elif archive_filename.endswith(('.tar.gz', '.tgz')):
1265 | format = 'tgz'
1266 | mode = 'r:gz'
1267 | elif archive_filename.endswith(('.tar.bz2', '.tbz')):
1268 | format = 'tbz'
1269 | mode = 'r:bz2'
1270 | elif archive_filename.endswith('.tar'):
1271 | format = 'tar'
1272 | mode = 'r'
1273 | else: # pragma: no cover
1274 | raise ValueError('Unknown format for %r' % archive_filename)
1275 | try:
1276 | if format == 'zip':
1277 | archive = ZipFile(archive_filename, 'r')
1278 | if check:
1279 | names = archive.namelist()
1280 | for name in names:
1281 | check_path(name)
1282 | else:
1283 | archive = tarfile.open(archive_filename, mode)
1284 | if check:
1285 | names = archive.getnames()
1286 | for name in names:
1287 | check_path(name)
1288 | if format != 'zip' and sys.version_info[0] < 3:
1289 | # See Python issue 17153. If the dest path contains Unicode,
1290 | # tarfile extraction fails on Python 2.x if a member path name
1291 | # contains non-ASCII characters - it leads to an implicit
1292 | # bytes -> unicode conversion using ASCII to decode.
1293 | for tarinfo in archive.getmembers():
1294 | if not isinstance(tarinfo.name, text_type):
1295 | tarinfo.name = tarinfo.name.decode('utf-8')
1296 |
1297 | # Limit extraction of dangerous items, if this Python
1298 | # allows it easily. If not, just trust the input.
1299 | # See: https://docs.python.org/3/library/tarfile.html#extraction-filters
1300 | def extraction_filter(member, path):
1301 | """Run tarfile.tar_filter, but raise the expected ValueError"""
1302 | # This is only called if the current Python has tarfile filters
1303 | try:
1304 | return tarfile.tar_filter(member, path)
1305 | except tarfile.FilterError as exc:
1306 | raise ValueError(str(exc))
1307 |
1308 | archive.extraction_filter = extraction_filter
1309 |
1310 | archive.extractall(dest_dir)
1311 |
1312 | finally:
1313 | if archive:
1314 | archive.close()
1315 |
1316 |
1317 | def zip_dir(directory):
1318 | """zip a directory tree into a BytesIO object"""
1319 | result = io.BytesIO()
1320 | dlen = len(directory)
1321 | with ZipFile(result, "w") as zf:
1322 | for root, dirs, files in os.walk(directory):
1323 | for name in files:
1324 | full = os.path.join(root, name)
1325 | rel = root[dlen:]
1326 | dest = os.path.join(rel, name)
1327 | zf.write(full, dest)
1328 | return result
1329 |
1330 |
1331 | #
1332 | # Simple progress bar
1333 | #
1334 |
1335 | UNITS = ('', 'K', 'M', 'G', 'T', 'P')
1336 |
1337 |
1338 | class Progress(object):
1339 | unknown = 'UNKNOWN'
1340 |
1341 | def __init__(self, minval=0, maxval=100):
1342 | assert maxval is None or maxval >= minval
1343 | self.min = self.cur = minval
1344 | self.max = maxval
1345 | self.started = None
1346 | self.elapsed = 0
1347 | self.done = False
1348 |
1349 | def update(self, curval):
1350 | assert self.min <= curval
1351 | assert self.max is None or curval <= self.max
1352 | self.cur = curval
1353 | now = time.time()
1354 | if self.started is None:
1355 | self.started = now
1356 | else:
1357 | self.elapsed = now - self.started
1358 |
1359 | def increment(self, incr):
1360 | assert incr >= 0
1361 | self.update(self.cur + incr)
1362 |
1363 | def start(self):
1364 | self.update(self.min)
1365 | return self
1366 |
1367 | def stop(self):
1368 | if self.max is not None:
1369 | self.update(self.max)
1370 | self.done = True
1371 |
1372 | @property
1373 | def maximum(self):
1374 | return self.unknown if self.max is None else self.max
1375 |
1376 | @property
1377 | def percentage(self):
1378 | if self.done:
1379 | result = '100 %'
1380 | elif self.max is None:
1381 | result = ' ?? %'
1382 | else:
1383 | v = 100.0 * (self.cur - self.min) / (self.max - self.min)
1384 | result = '%3d %%' % v
1385 | return result
1386 |
1387 | def format_duration(self, duration):
1388 | if (duration <= 0) and self.max is None or self.cur == self.min:
1389 | result = '??:??:??'
1390 | # elif duration < 1:
1391 | # result = '--:--:--'
1392 | else:
1393 | result = time.strftime('%H:%M:%S', time.gmtime(duration))
1394 | return result
1395 |
1396 | @property
1397 | def ETA(self):
1398 | if self.done:
1399 | prefix = 'Done'
1400 | t = self.elapsed
1401 | # import pdb; pdb.set_trace()
1402 | else:
1403 | prefix = 'ETA '
1404 | if self.max is None:
1405 | t = -1
1406 | elif self.elapsed == 0 or (self.cur == self.min):
1407 | t = 0
1408 | else:
1409 | # import pdb; pdb.set_trace()
1410 | t = float(self.max - self.min)
1411 | t /= self.cur - self.min
1412 | t = (t - 1) * self.elapsed
1413 | return '%s: %s' % (prefix, self.format_duration(t))
1414 |
1415 | @property
1416 | def speed(self):
1417 | if self.elapsed == 0:
1418 | result = 0.0
1419 | else:
1420 | result = (self.cur - self.min) / self.elapsed
1421 | for unit in UNITS:
1422 | if result < 1000:
1423 | break
1424 | result /= 1000.0
1425 | return '%d %sB/s' % (result, unit)
1426 |
1427 |
1428 | #
1429 | # Glob functionality
1430 | #
1431 |
1432 | RICH_GLOB = re.compile(r'\{([^}]*)\}')
1433 | _CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
1434 | _CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$')
1435 |
1436 |
1437 | def iglob(path_glob):
1438 | """Extended globbing function that supports ** and {opt1,opt2,opt3}."""
1439 | if _CHECK_RECURSIVE_GLOB.search(path_glob):
1440 | msg = """invalid glob %r: recursive glob "**" must be used alone"""
1441 | raise ValueError(msg % path_glob)
1442 | if _CHECK_MISMATCH_SET.search(path_glob):
1443 | msg = """invalid glob %r: mismatching set marker '{' or '}'"""
1444 | raise ValueError(msg % path_glob)
1445 | return _iglob(path_glob)
1446 |
1447 |
1448 | def _iglob(path_glob):
1449 | rich_path_glob = RICH_GLOB.split(path_glob, 1)
1450 | if len(rich_path_glob) > 1:
1451 | assert len(rich_path_glob) == 3, rich_path_glob
1452 | prefix, set, suffix = rich_path_glob
1453 | for item in set.split(','):
1454 | for path in _iglob(''.join((prefix, item, suffix))):
1455 | yield path
1456 | else:
1457 | if '**' not in path_glob:
1458 | for item in std_iglob(path_glob):
1459 | yield item
1460 | else:
1461 | prefix, radical = path_glob.split('**', 1)
1462 | if prefix == '':
1463 | prefix = '.'
1464 | if radical == '':
1465 | radical = '*'
1466 | else:
1467 | # we support both
1468 | radical = radical.lstrip('/')
1469 | radical = radical.lstrip('\\')
1470 | for path, dir, files in os.walk(prefix):
1471 | path = os.path.normpath(path)
1472 | for fn in _iglob(os.path.join(path, radical)):
1473 | yield fn
1474 |
1475 |
1476 | if ssl:
1477 | from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname,
1478 | CertificateError)
1479 |
1480 | #
1481 | # HTTPSConnection which verifies certificates/matches domains
1482 | #
1483 |
1484 | class HTTPSConnection(httplib.HTTPSConnection):
1485 | ca_certs = None # set this to the path to the certs file (.pem)
1486 | check_domain = True # only used if ca_certs is not None
1487 |
1488 | # noinspection PyPropertyAccess
1489 | def connect(self):
1490 | sock = socket.create_connection((self.host, self.port),
1491 | self.timeout)
1492 | if getattr(self, '_tunnel_host', False):
1493 | self.sock = sock
1494 | self._tunnel()
1495 |
1496 | context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1497 | if hasattr(ssl, 'OP_NO_SSLv2'):
1498 | context.options |= ssl.OP_NO_SSLv2
1499 | if getattr(self, 'cert_file', None):
1500 | context.load_cert_chain(self.cert_file, self.key_file)
1501 | kwargs = {}
1502 | if self.ca_certs:
1503 | context.verify_mode = ssl.CERT_REQUIRED
1504 | context.load_verify_locations(cafile=self.ca_certs)
1505 | if getattr(ssl, 'HAS_SNI', False):
1506 | kwargs['server_hostname'] = self.host
1507 |
1508 | self.sock = context.wrap_socket(sock, **kwargs)
1509 | if self.ca_certs and self.check_domain:
1510 | try:
1511 | match_hostname(self.sock.getpeercert(), self.host)
1512 | logger.debug('Host verified: %s', self.host)
1513 | except CertificateError: # pragma: no cover
1514 | self.sock.shutdown(socket.SHUT_RDWR)
1515 | self.sock.close()
1516 | raise
1517 |
1518 | class HTTPSHandler(BaseHTTPSHandler):
1519 |
1520 | def __init__(self, ca_certs, check_domain=True):
1521 | BaseHTTPSHandler.__init__(self)
1522 | self.ca_certs = ca_certs
1523 | self.check_domain = check_domain
1524 |
1525 | def _conn_maker(self, *args, **kwargs):
1526 | """
1527 | This is called to create a connection instance. Normally you'd
1528 | pass a connection class to do_open, but it doesn't actually check for
1529 | a class, and just expects a callable. As long as we behave just as a
1530 | constructor would have, we should be OK. If it ever changes so that
1531 | we *must* pass a class, we'll create an UnsafeHTTPSConnection class
1532 | which just sets check_domain to False in the class definition, and
1533 | choose which one to pass to do_open.
1534 | """
1535 | result = HTTPSConnection(*args, **kwargs)
1536 | if self.ca_certs:
1537 | result.ca_certs = self.ca_certs
1538 | result.check_domain = self.check_domain
1539 | return result
1540 |
1541 | def https_open(self, req):
1542 | try:
1543 | return self.do_open(self._conn_maker, req)
1544 | except URLError as e:
1545 | if 'certificate verify failed' in str(e.reason):
1546 | raise CertificateError(
1547 | 'Unable to verify server certificate '
1548 | 'for %s' % req.host)
1549 | else:
1550 | raise
1551 |
1552 | #
1553 | # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The-
1554 | # Middle proxy using HTTP listens on port 443, or an index mistakenly serves
1555 | # HTML containing a http://xyz link when it should be https://xyz),
1556 | # you can use the following handler class, which does not allow HTTP traffic.
1557 | #
1558 | # It works by inheriting from HTTPHandler - so build_opener won't add a
1559 | # handler for HTTP itself.
1560 | #
1561 | class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler):
1562 |
1563 | def http_open(self, req):
1564 | raise URLError(
1565 | 'Unexpected HTTP request on what should be a secure '
1566 | 'connection: %s' % req)
1567 |
1568 |
1569 | #
1570 | # XML-RPC with timeouts
1571 | #
1572 | class Transport(xmlrpclib.Transport):
1573 |
1574 | def __init__(self, timeout, use_datetime=0):
1575 | self.timeout = timeout
1576 | xmlrpclib.Transport.__init__(self, use_datetime)
1577 |
1578 | def make_connection(self, host):
1579 | h, eh, x509 = self.get_host_info(host)
1580 | if not self._connection or host != self._connection[0]:
1581 | self._extra_headers = eh
1582 | self._connection = host, httplib.HTTPConnection(h)
1583 | return self._connection[1]
1584 |
1585 |
1586 | if ssl:
1587 |
1588 | class SafeTransport(xmlrpclib.SafeTransport):
1589 |
1590 | def __init__(self, timeout, use_datetime=0):
1591 | self.timeout = timeout
1592 | xmlrpclib.SafeTransport.__init__(self, use_datetime)
1593 |
1594 | def make_connection(self, host):
1595 | h, eh, kwargs = self.get_host_info(host)
1596 | if not kwargs:
1597 | kwargs = {}
1598 | kwargs['timeout'] = self.timeout
1599 | if not self._connection or host != self._connection[0]:
1600 | self._extra_headers = eh
1601 | self._connection = host, httplib.HTTPSConnection(
1602 | h, None, **kwargs)
1603 | return self._connection[1]
1604 |
1605 |
1606 | class ServerProxy(xmlrpclib.ServerProxy):
1607 |
1608 | def __init__(self, uri, **kwargs):
1609 | self.timeout = timeout = kwargs.pop('timeout', None)
1610 | # The above classes only come into play if a timeout
1611 | # is specified
1612 | if timeout is not None:
1613 | # scheme = splittype(uri) # deprecated as of Python 3.8
1614 | scheme = urlparse(uri)[0]
1615 | use_datetime = kwargs.get('use_datetime', 0)
1616 | if scheme == 'https':
1617 | tcls = SafeTransport
1618 | else:
1619 | tcls = Transport
1620 | kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime)
1621 | self.transport = t
1622 | xmlrpclib.ServerProxy.__init__(self, uri, **kwargs)
1623 |
1624 |
1625 | #
1626 | # CSV functionality. This is provided because on 2.x, the csv module can't
1627 | # handle Unicode. However, we need to deal with Unicode in e.g. RECORD files.
1628 | #
1629 |
1630 |
1631 | def _csv_open(fn, mode, **kwargs):
1632 | if sys.version_info[0] < 3:
1633 | mode += 'b'
1634 | else:
1635 | kwargs['newline'] = ''
1636 | # Python 3 determines encoding from locale. Force 'utf-8'
1637 | # file encoding to match other forced utf-8 encoding
1638 | kwargs['encoding'] = 'utf-8'
1639 | return open(fn, mode, **kwargs)
1640 |
1641 |
1642 | class CSVBase(object):
1643 | defaults = {
1644 | 'delimiter': str(','), # The strs are used because we need native
1645 | 'quotechar': str('"'), # str in the csv API (2.x won't take
1646 | 'lineterminator': str('\n') # Unicode)
1647 | }
1648 |
1649 | def __enter__(self):
1650 | return self
1651 |
1652 | def __exit__(self, *exc_info):
1653 | self.stream.close()
1654 |
1655 |
1656 | class CSVReader(CSVBase):
1657 |
1658 | def __init__(self, **kwargs):
1659 | if 'stream' in kwargs:
1660 | stream = kwargs['stream']
1661 | if sys.version_info[0] >= 3:
1662 | # needs to be a text stream
1663 | stream = codecs.getreader('utf-8')(stream)
1664 | self.stream = stream
1665 | else:
1666 | self.stream = _csv_open(kwargs['path'], 'r')
1667 | self.reader = csv.reader(self.stream, **self.defaults)
1668 |
1669 | def __iter__(self):
1670 | return self
1671 |
1672 | def next(self):
1673 | result = next(self.reader)
1674 | if sys.version_info[0] < 3:
1675 | for i, item in enumerate(result):
1676 | if not isinstance(item, text_type):
1677 | result[i] = item.decode('utf-8')
1678 | return result
1679 |
1680 | __next__ = next
1681 |
1682 |
1683 | class CSVWriter(CSVBase):
1684 |
1685 | def __init__(self, fn, **kwargs):
1686 | self.stream = _csv_open(fn, 'w')
1687 | self.writer = csv.writer(self.stream, **self.defaults)
1688 |
1689 | def writerow(self, row):
1690 | if sys.version_info[0] < 3:
1691 | r = []
1692 | for item in row:
1693 | if isinstance(item, text_type):
1694 | item = item.encode('utf-8')
1695 | r.append(item)
1696 | row = r
1697 | self.writer.writerow(row)
1698 |
1699 |
1700 | #
1701 | # Configurator functionality
1702 | #
1703 |
1704 |
1705 | class Configurator(BaseConfigurator):
1706 |
1707 | value_converters = dict(BaseConfigurator.value_converters)
1708 | value_converters['inc'] = 'inc_convert'
1709 |
1710 | def __init__(self, config, base=None):
1711 | super(Configurator, self).__init__(config)
1712 | self.base = base or os.getcwd()
1713 |
1714 | def configure_custom(self, config):
1715 |
1716 | def convert(o):
1717 | if isinstance(o, (list, tuple)):
1718 | result = type(o)([convert(i) for i in o])
1719 | elif isinstance(o, dict):
1720 | if '()' in o:
1721 | result = self.configure_custom(o)
1722 | else:
1723 | result = {}
1724 | for k in o:
1725 | result[k] = convert(o[k])
1726 | else:
1727 | result = self.convert(o)
1728 | return result
1729 |
1730 | c = config.pop('()')
1731 | if not callable(c):
1732 | c = self.resolve(c)
1733 | props = config.pop('.', None)
1734 | # Check for valid identifiers
1735 | args = config.pop('[]', ())
1736 | if args:
1737 | args = tuple([convert(o) for o in args])
1738 | items = [(k, convert(config[k])) for k in config if valid_ident(k)]
1739 | kwargs = dict(items)
1740 | result = c(*args, **kwargs)
1741 | if props:
1742 | for n, v in props.items():
1743 | setattr(result, n, convert(v))
1744 | return result
1745 |
1746 | def __getitem__(self, key):
1747 | result = self.config[key]
1748 | if isinstance(result, dict) and '()' in result:
1749 | self.config[key] = result = self.configure_custom(result)
1750 | return result
1751 |
1752 | def inc_convert(self, value):
1753 | """Default converter for the inc:// protocol."""
1754 | if not os.path.isabs(value):
1755 | value = os.path.join(self.base, value)
1756 | with codecs.open(value, 'r', encoding='utf-8') as f:
1757 | result = json.load(f)
1758 | return result
1759 |
1760 |
1761 | class SubprocessMixin(object):
1762 | """
1763 | Mixin for running subprocesses and capturing their output
1764 | """
1765 |
1766 | def __init__(self, verbose=False, progress=None):
1767 | self.verbose = verbose
1768 | self.progress = progress
1769 |
1770 | def reader(self, stream, context):
1771 | """
1772 | Read lines from a subprocess' output stream and either pass to a progress
1773 | callable (if specified) or write progress information to sys.stderr.
1774 | """
1775 | progress = self.progress
1776 | verbose = self.verbose
1777 | while True:
1778 | s = stream.readline()
1779 | if not s:
1780 | break
1781 | if progress is not None:
1782 | progress(s, context)
1783 | else:
1784 | if not verbose:
1785 | sys.stderr.write('.')
1786 | else:
1787 | sys.stderr.write(s.decode('utf-8'))
1788 | sys.stderr.flush()
1789 | stream.close()
1790 |
1791 | def run_command(self, cmd, **kwargs):
1792 | p = subprocess.Popen(cmd,
1793 | stdout=subprocess.PIPE,
1794 | stderr=subprocess.PIPE,
1795 | **kwargs)
1796 | t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout'))
1797 | t1.start()
1798 | t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr'))
1799 | t2.start()
1800 | p.wait()
1801 | t1.join()
1802 | t2.join()
1803 | if self.progress is not None:
1804 | self.progress('done.', 'main')
1805 | elif self.verbose:
1806 | sys.stderr.write('done.\n')
1807 | return p
1808 |
1809 |
1810 | def normalize_name(name):
1811 | """Normalize a python package name a la PEP 503"""
1812 | # https://www.python.org/dev/peps/pep-0503/#normalized-names
1813 | return re.sub('[-_.]+', '-', name).lower()
1814 |
1815 |
1816 | # def _get_pypirc_command():
1817 | # """
1818 | # Get the distutils command for interacting with PyPI configurations.
1819 | # :return: the command.
1820 | # """
1821 | # from distutils.core import Distribution
1822 | # from distutils.config import PyPIRCCommand
1823 | # d = Distribution()
1824 | # return PyPIRCCommand(d)
1825 |
1826 |
1827 | class PyPIRCFile(object):
1828 |
1829 | DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
1830 | DEFAULT_REALM = 'pypi'
1831 |
1832 | def __init__(self, fn=None, url=None):
1833 | if fn is None:
1834 | fn = os.path.join(os.path.expanduser('~'), '.pypirc')
1835 | self.filename = fn
1836 | self.url = url
1837 |
1838 | def read(self):
1839 | result = {}
1840 |
1841 | if os.path.exists(self.filename):
1842 | repository = self.url or self.DEFAULT_REPOSITORY
1843 |
1844 | config = configparser.RawConfigParser()
1845 | config.read(self.filename)
1846 | sections = config.sections()
1847 | if 'distutils' in sections:
1848 | # let's get the list of servers
1849 | index_servers = config.get('distutils', 'index-servers')
1850 | _servers = [
1851 | server.strip() for server in index_servers.split('\n')
1852 | if server.strip() != ''
1853 | ]
1854 | if _servers == []:
1855 | # nothing set, let's try to get the default pypi
1856 | if 'pypi' in sections:
1857 | _servers = ['pypi']
1858 | else:
1859 | for server in _servers:
1860 | result = {'server': server}
1861 | result['username'] = config.get(server, 'username')
1862 |
1863 | # optional params
1864 | for key, default in (('repository',
1865 | self.DEFAULT_REPOSITORY),
1866 | ('realm', self.DEFAULT_REALM),
1867 | ('password', None)):
1868 | if config.has_option(server, key):
1869 | result[key] = config.get(server, key)
1870 | else:
1871 | result[key] = default
1872 |
1873 | # work around people having "repository" for the "pypi"
1874 | # section of their config set to the HTTP (rather than
1875 | # HTTPS) URL
1876 | if (server == 'pypi' and repository
1877 | in (self.DEFAULT_REPOSITORY, 'pypi')):
1878 | result['repository'] = self.DEFAULT_REPOSITORY
1879 | elif (result['server'] != repository
1880 | and result['repository'] != repository):
1881 | result = {}
1882 | elif 'server-login' in sections:
1883 | # old format
1884 | server = 'server-login'
1885 | if config.has_option(server, 'repository'):
1886 | repository = config.get(server, 'repository')
1887 | else:
1888 | repository = self.DEFAULT_REPOSITORY
1889 | result = {
1890 | 'username': config.get(server, 'username'),
1891 | 'password': config.get(server, 'password'),
1892 | 'repository': repository,
1893 | 'server': server,
1894 | 'realm': self.DEFAULT_REALM
1895 | }
1896 | return result
1897 |
1898 | def update(self, username, password):
1899 | # import pdb; pdb.set_trace()
1900 | config = configparser.RawConfigParser()
1901 | fn = self.filename
1902 | config.read(fn)
1903 | if not config.has_section('pypi'):
1904 | config.add_section('pypi')
1905 | config.set('pypi', 'username', username)
1906 | config.set('pypi', 'password', password)
1907 | with open(fn, 'w') as f:
1908 | config.write(f)
1909 |
1910 |
1911 | def _load_pypirc(index):
1912 | """
1913 | Read the PyPI access configuration as supported by distutils.
1914 | """
1915 | return PyPIRCFile(url=index.url).read()
1916 |
1917 |
1918 | def _store_pypirc(index):
1919 | PyPIRCFile().update(index.username, index.password)
1920 |
1921 |
1922 | #
1923 | # get_platform()/get_host_platform() copied from Python 3.10.a0 source, with some minor
1924 | # tweaks
1925 | #
1926 |
1927 |
1928 | def get_host_platform():
1929 | """Return a string that identifies the current platform. This is used mainly to
1930 | distinguish platform-specific build directories and platform-specific built
1931 | distributions. Typically includes the OS name and version and the
1932 | architecture (as supplied by 'os.uname()'), although the exact information
1933 | included depends on the OS; eg. on Linux, the kernel version isn't
1934 | particularly important.
1935 |
1936 | Examples of returned values:
1937 | linux-i586
1938 | linux-alpha (?)
1939 | solaris-2.6-sun4u
1940 |
1941 | Windows will return one of:
1942 | win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
1943 | win32 (all others - specifically, sys.platform is returned)
1944 |
1945 | For other non-POSIX platforms, currently just returns 'sys.platform'.
1946 |
1947 | """
1948 | if os.name == 'nt':
1949 | if 'amd64' in sys.version.lower():
1950 | return 'win-amd64'
1951 | if '(arm)' in sys.version.lower():
1952 | return 'win-arm32'
1953 | if '(arm64)' in sys.version.lower():
1954 | return 'win-arm64'
1955 | return sys.platform
1956 |
1957 | # Set for cross builds explicitly
1958 | if "_PYTHON_HOST_PLATFORM" in os.environ:
1959 | return os.environ["_PYTHON_HOST_PLATFORM"]
1960 |
1961 | if os.name != 'posix' or not hasattr(os, 'uname'):
1962 | # XXX what about the architecture? NT is Intel or Alpha,
1963 | # Mac OS is M68k or PPC, etc.
1964 | return sys.platform
1965 |
1966 | # Try to distinguish various flavours of Unix
1967 |
1968 | (osname, host, release, version, machine) = os.uname()
1969 |
1970 | # Convert the OS name to lowercase, remove '/' characters, and translate
1971 | # spaces (for "Power Macintosh")
1972 | osname = osname.lower().replace('/', '')
1973 | machine = machine.replace(' ', '_').replace('/', '-')
1974 |
1975 | if osname[:5] == 'linux':
1976 | # At least on Linux/Intel, 'machine' is the processor --
1977 | # i386, etc.
1978 | # XXX what about Alpha, SPARC, etc?
1979 | return "%s-%s" % (osname, machine)
1980 |
1981 | elif osname[:5] == 'sunos':
1982 | if release[0] >= '5': # SunOS 5 == Solaris 2
1983 | osname = 'solaris'
1984 | release = '%d.%s' % (int(release[0]) - 3, release[2:])
1985 | # We can't use 'platform.architecture()[0]' because a
1986 | # bootstrap problem. We use a dict to get an error
1987 | # if some suspicious happens.
1988 | bitness = {2147483647: '32bit', 9223372036854775807: '64bit'}
1989 | machine += '.%s' % bitness[sys.maxsize]
1990 | # fall through to standard osname-release-machine representation
1991 | elif osname[:3] == 'aix':
1992 | from _aix_support import aix_platform
1993 | return aix_platform()
1994 | elif osname[:6] == 'cygwin':
1995 | osname = 'cygwin'
1996 | rel_re = re.compile(r'[\d.]+', re.ASCII)
1997 | m = rel_re.match(release)
1998 | if m:
1999 | release = m.group()
2000 | elif osname[:6] == 'darwin':
2001 | import _osx_support
2002 | try:
2003 | from distutils import sysconfig
2004 | except ImportError:
2005 | import sysconfig
2006 | osname, release, machine = _osx_support.get_platform_osx(
2007 | sysconfig.get_config_vars(), osname, release, machine)
2008 |
2009 | return '%s-%s-%s' % (osname, release, machine)
2010 |
2011 |
2012 | _TARGET_TO_PLAT = {
2013 | 'x86': 'win32',
2014 | 'x64': 'win-amd64',
2015 | 'arm': 'win-arm32',
2016 | }
2017 |
2018 |
2019 | def get_platform():
2020 | if os.name != 'nt':
2021 | return get_host_platform()
2022 | cross_compilation_target = os.environ.get('VSCMD_ARG_TGT_ARCH')
2023 | if cross_compilation_target not in _TARGET_TO_PLAT:
2024 | return get_host_platform()
2025 | return _TARGET_TO_PLAT[cross_compilation_target]
2026 |
```