This is page 145 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/database.py:
--------------------------------------------------------------------------------
```python
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright (C) 2012-2023 The Python Software Foundation.
4 | # See LICENSE.txt and CONTRIBUTORS.txt.
5 | #
6 | """PEP 376 implementation."""
7 |
8 | from __future__ import unicode_literals
9 |
10 | import base64
11 | import codecs
12 | import contextlib
13 | import hashlib
14 | import logging
15 | import os
16 | import posixpath
17 | import sys
18 | import zipimport
19 |
20 | from . import DistlibException, resources
21 | from .compat import StringIO
22 | from .version import get_scheme, UnsupportedVersionError
23 | from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME,
24 | LEGACY_METADATA_FILENAME)
25 | from .util import (parse_requirement, cached_property, parse_name_and_version,
26 | read_exports, write_exports, CSVReader, CSVWriter)
27 |
28 | __all__ = [
29 | 'Distribution', 'BaseInstalledDistribution', 'InstalledDistribution',
30 | 'EggInfoDistribution', 'DistributionPath'
31 | ]
32 |
33 | logger = logging.getLogger(__name__)
34 |
35 | EXPORTS_FILENAME = 'pydist-exports.json'
36 | COMMANDS_FILENAME = 'pydist-commands.json'
37 |
38 | DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED',
39 | 'RESOURCES', EXPORTS_FILENAME, 'SHARED')
40 |
41 | DISTINFO_EXT = '.dist-info'
42 |
43 |
44 | class _Cache(object):
45 | """
46 | A simple cache mapping names and .dist-info paths to distributions
47 | """
48 |
49 | def __init__(self):
50 | """
51 | Initialise an instance. There is normally one for each DistributionPath.
52 | """
53 | self.name = {}
54 | self.path = {}
55 | self.generated = False
56 |
57 | def clear(self):
58 | """
59 | Clear the cache, setting it to its initial state.
60 | """
61 | self.name.clear()
62 | self.path.clear()
63 | self.generated = False
64 |
65 | def add(self, dist):
66 | """
67 | Add a distribution to the cache.
68 | :param dist: The distribution to add.
69 | """
70 | if dist.path not in self.path:
71 | self.path[dist.path] = dist
72 | self.name.setdefault(dist.key, []).append(dist)
73 |
74 |
75 | class DistributionPath(object):
76 | """
77 | Represents a set of distributions installed on a path (typically sys.path).
78 | """
79 |
80 | def __init__(self, path=None, include_egg=False):
81 | """
82 | Create an instance from a path, optionally including legacy (distutils/
83 | setuptools/distribute) distributions.
84 | :param path: The path to use, as a list of directories. If not specified,
85 | sys.path is used.
86 | :param include_egg: If True, this instance will look for and return legacy
87 | distributions as well as those based on PEP 376.
88 | """
89 | if path is None:
90 | path = sys.path
91 | self.path = path
92 | self._include_dist = True
93 | self._include_egg = include_egg
94 |
95 | self._cache = _Cache()
96 | self._cache_egg = _Cache()
97 | self._cache_enabled = True
98 | self._scheme = get_scheme('default')
99 |
100 | def _get_cache_enabled(self):
101 | return self._cache_enabled
102 |
103 | def _set_cache_enabled(self, value):
104 | self._cache_enabled = value
105 |
106 | cache_enabled = property(_get_cache_enabled, _set_cache_enabled)
107 |
108 | def clear_cache(self):
109 | """
110 | Clears the internal cache.
111 | """
112 | self._cache.clear()
113 | self._cache_egg.clear()
114 |
115 | def _yield_distributions(self):
116 | """
117 | Yield .dist-info and/or .egg(-info) distributions.
118 | """
119 | # We need to check if we've seen some resources already, because on
120 | # some Linux systems (e.g. some Debian/Ubuntu variants) there are
121 | # symlinks which alias other files in the environment.
122 | seen = set()
123 | for path in self.path:
124 | finder = resources.finder_for_path(path)
125 | if finder is None:
126 | continue
127 | r = finder.find('')
128 | if not r or not r.is_container:
129 | continue
130 | rset = sorted(r.resources)
131 | for entry in rset:
132 | r = finder.find(entry)
133 | if not r or r.path in seen:
134 | continue
135 | try:
136 | if self._include_dist and entry.endswith(DISTINFO_EXT):
137 | possible_filenames = [
138 | METADATA_FILENAME, WHEEL_METADATA_FILENAME,
139 | LEGACY_METADATA_FILENAME
140 | ]
141 | for metadata_filename in possible_filenames:
142 | metadata_path = posixpath.join(
143 | entry, metadata_filename)
144 | pydist = finder.find(metadata_path)
145 | if pydist:
146 | break
147 | else:
148 | continue
149 |
150 | with contextlib.closing(pydist.as_stream()) as stream:
151 | metadata = Metadata(fileobj=stream,
152 | scheme='legacy')
153 | logger.debug('Found %s', r.path)
154 | seen.add(r.path)
155 | yield new_dist_class(r.path,
156 | metadata=metadata,
157 | env=self)
158 | elif self._include_egg and entry.endswith(
159 | ('.egg-info', '.egg')):
160 | logger.debug('Found %s', r.path)
161 | seen.add(r.path)
162 | yield old_dist_class(r.path, self)
163 | except Exception as e:
164 | msg = 'Unable to read distribution at %s, perhaps due to bad metadata: %s'
165 | logger.warning(msg, r.path, e)
166 | import warnings
167 | warnings.warn(msg % (r.path, e), stacklevel=2)
168 |
169 | def _generate_cache(self):
170 | """
171 | Scan the path for distributions and populate the cache with
172 | those that are found.
173 | """
174 | gen_dist = not self._cache.generated
175 | gen_egg = self._include_egg and not self._cache_egg.generated
176 | if gen_dist or gen_egg:
177 | for dist in self._yield_distributions():
178 | if isinstance(dist, InstalledDistribution):
179 | self._cache.add(dist)
180 | else:
181 | self._cache_egg.add(dist)
182 |
183 | if gen_dist:
184 | self._cache.generated = True
185 | if gen_egg:
186 | self._cache_egg.generated = True
187 |
188 | @classmethod
189 | def distinfo_dirname(cls, name, version):
190 | """
191 | The *name* and *version* parameters are converted into their
192 | filename-escaped form, i.e. any ``'-'`` characters are replaced
193 | with ``'_'`` other than the one in ``'dist-info'`` and the one
194 | separating the name from the version number.
195 |
196 | :parameter name: is converted to a standard distribution name by replacing
197 | any runs of non- alphanumeric characters with a single
198 | ``'-'``.
199 | :type name: string
200 | :parameter version: is converted to a standard version string. Spaces
201 | become dots, and all other non-alphanumeric characters
202 | (except dots) become dashes, with runs of multiple
203 | dashes condensed to a single dash.
204 | :type version: string
205 | :returns: directory name
206 | :rtype: string"""
207 | name = name.replace('-', '_')
208 | return '-'.join([name, version]) + DISTINFO_EXT
209 |
210 | def get_distributions(self):
211 | """
212 | Provides an iterator that looks for distributions and returns
213 | :class:`InstalledDistribution` or
214 | :class:`EggInfoDistribution` instances for each one of them.
215 |
216 | :rtype: iterator of :class:`InstalledDistribution` and
217 | :class:`EggInfoDistribution` instances
218 | """
219 | if not self._cache_enabled:
220 | for dist in self._yield_distributions():
221 | yield dist
222 | else:
223 | self._generate_cache()
224 |
225 | for dist in self._cache.path.values():
226 | yield dist
227 |
228 | if self._include_egg:
229 | for dist in self._cache_egg.path.values():
230 | yield dist
231 |
232 | def get_distribution(self, name):
233 | """
234 | Looks for a named distribution on the path.
235 |
236 | This function only returns the first result found, as no more than one
237 | value is expected. If nothing is found, ``None`` is returned.
238 |
239 | :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution`
240 | or ``None``
241 | """
242 | result = None
243 | name = name.lower()
244 | if not self._cache_enabled:
245 | for dist in self._yield_distributions():
246 | if dist.key == name:
247 | result = dist
248 | break
249 | else:
250 | self._generate_cache()
251 |
252 | if name in self._cache.name:
253 | result = self._cache.name[name][0]
254 | elif self._include_egg and name in self._cache_egg.name:
255 | result = self._cache_egg.name[name][0]
256 | return result
257 |
258 | def provides_distribution(self, name, version=None):
259 | """
260 | Iterates over all distributions to find which distributions provide *name*.
261 | If a *version* is provided, it will be used to filter the results.
262 |
263 | This function only returns the first result found, since no more than
264 | one values are expected. If the directory is not found, returns ``None``.
265 |
266 | :parameter version: a version specifier that indicates the version
267 | required, conforming to the format in ``PEP-345``
268 |
269 | :type name: string
270 | :type version: string
271 | """
272 | matcher = None
273 | if version is not None:
274 | try:
275 | matcher = self._scheme.matcher('%s (%s)' % (name, version))
276 | except ValueError:
277 | raise DistlibException('invalid name or version: %r, %r' %
278 | (name, version))
279 |
280 | for dist in self.get_distributions():
281 | # We hit a problem on Travis where enum34 was installed and doesn't
282 | # have a provides attribute ...
283 | if not hasattr(dist, 'provides'):
284 | logger.debug('No "provides": %s', dist)
285 | else:
286 | provided = dist.provides
287 |
288 | for p in provided:
289 | p_name, p_ver = parse_name_and_version(p)
290 | if matcher is None:
291 | if p_name == name:
292 | yield dist
293 | break
294 | else:
295 | if p_name == name and matcher.match(p_ver):
296 | yield dist
297 | break
298 |
299 | def get_file_path(self, name, relative_path):
300 | """
301 | Return the path to a resource file.
302 | """
303 | dist = self.get_distribution(name)
304 | if dist is None:
305 | raise LookupError('no distribution named %r found' % name)
306 | return dist.get_resource_path(relative_path)
307 |
308 | def get_exported_entries(self, category, name=None):
309 | """
310 | Return all of the exported entries in a particular category.
311 |
312 | :param category: The category to search for entries.
313 | :param name: If specified, only entries with that name are returned.
314 | """
315 | for dist in self.get_distributions():
316 | r = dist.exports
317 | if category in r:
318 | d = r[category]
319 | if name is not None:
320 | if name in d:
321 | yield d[name]
322 | else:
323 | for v in d.values():
324 | yield v
325 |
326 |
327 | class Distribution(object):
328 | """
329 | A base class for distributions, whether installed or from indexes.
330 | Either way, it must have some metadata, so that's all that's needed
331 | for construction.
332 | """
333 |
334 | build_time_dependency = False
335 | """
336 | Set to True if it's known to be only a build-time dependency (i.e.
337 | not needed after installation).
338 | """
339 |
340 | requested = False
341 | """A boolean that indicates whether the ``REQUESTED`` metadata file is
342 | present (in other words, whether the package was installed by user
343 | request or it was installed as a dependency)."""
344 |
345 | def __init__(self, metadata):
346 | """
347 | Initialise an instance.
348 | :param metadata: The instance of :class:`Metadata` describing this
349 | distribution.
350 | """
351 | self.metadata = metadata
352 | self.name = metadata.name
353 | self.key = self.name.lower() # for case-insensitive comparisons
354 | self.version = metadata.version
355 | self.locator = None
356 | self.digest = None
357 | self.extras = None # additional features requested
358 | self.context = None # environment marker overrides
359 | self.download_urls = set()
360 | self.digests = {}
361 |
362 | @property
363 | def source_url(self):
364 | """
365 | The source archive download URL for this distribution.
366 | """
367 | return self.metadata.source_url
368 |
369 | download_url = source_url # Backward compatibility
370 |
371 | @property
372 | def name_and_version(self):
373 | """
374 | A utility property which displays the name and version in parentheses.
375 | """
376 | return '%s (%s)' % (self.name, self.version)
377 |
378 | @property
379 | def provides(self):
380 | """
381 | A set of distribution names and versions provided by this distribution.
382 | :return: A set of "name (version)" strings.
383 | """
384 | plist = self.metadata.provides
385 | s = '%s (%s)' % (self.name, self.version)
386 | if s not in plist:
387 | plist.append(s)
388 | return plist
389 |
390 | def _get_requirements(self, req_attr):
391 | md = self.metadata
392 | reqts = getattr(md, req_attr)
393 | logger.debug('%s: got requirements %r from metadata: %r', self.name,
394 | req_attr, reqts)
395 | return set(
396 | md.get_requirements(reqts, extras=self.extras, env=self.context))
397 |
398 | @property
399 | def run_requires(self):
400 | return self._get_requirements('run_requires')
401 |
402 | @property
403 | def meta_requires(self):
404 | return self._get_requirements('meta_requires')
405 |
406 | @property
407 | def build_requires(self):
408 | return self._get_requirements('build_requires')
409 |
410 | @property
411 | def test_requires(self):
412 | return self._get_requirements('test_requires')
413 |
414 | @property
415 | def dev_requires(self):
416 | return self._get_requirements('dev_requires')
417 |
418 | def matches_requirement(self, req):
419 | """
420 | Say if this instance matches (fulfills) a requirement.
421 | :param req: The requirement to match.
422 | :rtype req: str
423 | :return: True if it matches, else False.
424 | """
425 | # Requirement may contain extras - parse to lose those
426 | # from what's passed to the matcher
427 | r = parse_requirement(req)
428 | scheme = get_scheme(self.metadata.scheme)
429 | try:
430 | matcher = scheme.matcher(r.requirement)
431 | except UnsupportedVersionError:
432 | # XXX compat-mode if cannot read the version
433 | logger.warning('could not read version %r - using name only', req)
434 | name = req.split()[0]
435 | matcher = scheme.matcher(name)
436 |
437 | name = matcher.key # case-insensitive
438 |
439 | result = False
440 | for p in self.provides:
441 | p_name, p_ver = parse_name_and_version(p)
442 | if p_name != name:
443 | continue
444 | try:
445 | result = matcher.match(p_ver)
446 | break
447 | except UnsupportedVersionError:
448 | pass
449 | return result
450 |
451 | def __repr__(self):
452 | """
453 | Return a textual representation of this instance,
454 | """
455 | if self.source_url:
456 | suffix = ' [%s]' % self.source_url
457 | else:
458 | suffix = ''
459 | return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix)
460 |
461 | def __eq__(self, other):
462 | """
463 | See if this distribution is the same as another.
464 | :param other: The distribution to compare with. To be equal to one
465 | another. distributions must have the same type, name,
466 | version and source_url.
467 | :return: True if it is the same, else False.
468 | """
469 | if type(other) is not type(self):
470 | result = False
471 | else:
472 | result = (self.name == other.name and self.version == other.version
473 | and self.source_url == other.source_url)
474 | return result
475 |
476 | def __hash__(self):
477 | """
478 | Compute hash in a way which matches the equality test.
479 | """
480 | return hash(self.name) + hash(self.version) + hash(self.source_url)
481 |
482 |
483 | class BaseInstalledDistribution(Distribution):
484 | """
485 | This is the base class for installed distributions (whether PEP 376 or
486 | legacy).
487 | """
488 |
489 | hasher = None
490 |
491 | def __init__(self, metadata, path, env=None):
492 | """
493 | Initialise an instance.
494 | :param metadata: An instance of :class:`Metadata` which describes the
495 | distribution. This will normally have been initialised
496 | from a metadata file in the ``path``.
497 | :param path: The path of the ``.dist-info`` or ``.egg-info``
498 | directory for the distribution.
499 | :param env: This is normally the :class:`DistributionPath`
500 | instance where this distribution was found.
501 | """
502 | super(BaseInstalledDistribution, self).__init__(metadata)
503 | self.path = path
504 | self.dist_path = env
505 |
506 | def get_hash(self, data, hasher=None):
507 | """
508 | Get the hash of some data, using a particular hash algorithm, if
509 | specified.
510 |
511 | :param data: The data to be hashed.
512 | :type data: bytes
513 | :param hasher: The name of a hash implementation, supported by hashlib,
514 | or ``None``. Examples of valid values are ``'sha1'``,
515 | ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and
516 | ``'sha512'``. If no hasher is specified, the ``hasher``
517 | attribute of the :class:`InstalledDistribution` instance
518 | is used. If the hasher is determined to be ``None``, MD5
519 | is used as the hashing algorithm.
520 | :returns: The hash of the data. If a hasher was explicitly specified,
521 | the returned hash will be prefixed with the specified hasher
522 | followed by '='.
523 | :rtype: str
524 | """
525 | if hasher is None:
526 | hasher = self.hasher
527 | if hasher is None:
528 | hasher = hashlib.md5
529 | prefix = ''
530 | else:
531 | hasher = getattr(hashlib, hasher)
532 | prefix = '%s=' % self.hasher
533 | digest = hasher(data).digest()
534 | digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii')
535 | return '%s%s' % (prefix, digest)
536 |
537 |
538 | class InstalledDistribution(BaseInstalledDistribution):
539 | """
540 | Created with the *path* of the ``.dist-info`` directory provided to the
541 | constructor. It reads the metadata contained in ``pydist.json`` when it is
542 | instantiated., or uses a passed in Metadata instance (useful for when
543 | dry-run mode is being used).
544 | """
545 |
546 | hasher = 'sha256'
547 |
548 | def __init__(self, path, metadata=None, env=None):
549 | self.modules = []
550 | self.finder = finder = resources.finder_for_path(path)
551 | if finder is None:
552 | raise ValueError('finder unavailable for %s' % path)
553 | if env and env._cache_enabled and path in env._cache.path:
554 | metadata = env._cache.path[path].metadata
555 | elif metadata is None:
556 | r = finder.find(METADATA_FILENAME)
557 | # Temporary - for Wheel 0.23 support
558 | if r is None:
559 | r = finder.find(WHEEL_METADATA_FILENAME)
560 | # Temporary - for legacy support
561 | if r is None:
562 | r = finder.find(LEGACY_METADATA_FILENAME)
563 | if r is None:
564 | raise ValueError('no %s found in %s' %
565 | (METADATA_FILENAME, path))
566 | with contextlib.closing(r.as_stream()) as stream:
567 | metadata = Metadata(fileobj=stream, scheme='legacy')
568 |
569 | super(InstalledDistribution, self).__init__(metadata, path, env)
570 |
571 | if env and env._cache_enabled:
572 | env._cache.add(self)
573 |
574 | r = finder.find('REQUESTED')
575 | self.requested = r is not None
576 | p = os.path.join(path, 'top_level.txt')
577 | if os.path.exists(p):
578 | with open(p, 'rb') as f:
579 | data = f.read().decode('utf-8')
580 | self.modules = data.splitlines()
581 |
582 | def __repr__(self):
583 | return '<InstalledDistribution %r %s at %r>' % (
584 | self.name, self.version, self.path)
585 |
586 | def __str__(self):
587 | return "%s %s" % (self.name, self.version)
588 |
589 | def _get_records(self):
590 | """
591 | Get the list of installed files for the distribution
592 | :return: A list of tuples of path, hash and size. Note that hash and
593 | size might be ``None`` for some entries. The path is exactly
594 | as stored in the file (which is as in PEP 376).
595 | """
596 | results = []
597 | r = self.get_distinfo_resource('RECORD')
598 | with contextlib.closing(r.as_stream()) as stream:
599 | with CSVReader(stream=stream) as record_reader:
600 | # Base location is parent dir of .dist-info dir
601 | # base_location = os.path.dirname(self.path)
602 | # base_location = os.path.abspath(base_location)
603 | for row in record_reader:
604 | missing = [None for i in range(len(row), 3)]
605 | path, checksum, size = row + missing
606 | # if not os.path.isabs(path):
607 | # path = path.replace('/', os.sep)
608 | # path = os.path.join(base_location, path)
609 | results.append((path, checksum, size))
610 | return results
611 |
612 | @cached_property
613 | def exports(self):
614 | """
615 | Return the information exported by this distribution.
616 | :return: A dictionary of exports, mapping an export category to a dict
617 | of :class:`ExportEntry` instances describing the individual
618 | export entries, and keyed by name.
619 | """
620 | result = {}
621 | r = self.get_distinfo_resource(EXPORTS_FILENAME)
622 | if r:
623 | result = self.read_exports()
624 | return result
625 |
626 | def read_exports(self):
627 | """
628 | Read exports data from a file in .ini format.
629 |
630 | :return: A dictionary of exports, mapping an export category to a list
631 | of :class:`ExportEntry` instances describing the individual
632 | export entries.
633 | """
634 | result = {}
635 | r = self.get_distinfo_resource(EXPORTS_FILENAME)
636 | if r:
637 | with contextlib.closing(r.as_stream()) as stream:
638 | result = read_exports(stream)
639 | return result
640 |
641 | def write_exports(self, exports):
642 | """
643 | Write a dictionary of exports to a file in .ini format.
644 | :param exports: A dictionary of exports, mapping an export category to
645 | a list of :class:`ExportEntry` instances describing the
646 | individual export entries.
647 | """
648 | rf = self.get_distinfo_file(EXPORTS_FILENAME)
649 | with open(rf, 'w') as f:
650 | write_exports(exports, f)
651 |
652 | def get_resource_path(self, relative_path):
653 | """
654 | NOTE: This API may change in the future.
655 |
656 | Return the absolute path to a resource file with the given relative
657 | path.
658 |
659 | :param relative_path: The path, relative to .dist-info, of the resource
660 | of interest.
661 | :return: The absolute path where the resource is to be found.
662 | """
663 | r = self.get_distinfo_resource('RESOURCES')
664 | with contextlib.closing(r.as_stream()) as stream:
665 | with CSVReader(stream=stream) as resources_reader:
666 | for relative, destination in resources_reader:
667 | if relative == relative_path:
668 | return destination
669 | raise KeyError('no resource file with relative path %r '
670 | 'is installed' % relative_path)
671 |
672 | def list_installed_files(self):
673 | """
674 | Iterates over the ``RECORD`` entries and returns a tuple
675 | ``(path, hash, size)`` for each line.
676 |
677 | :returns: iterator of (path, hash, size)
678 | """
679 | for result in self._get_records():
680 | yield result
681 |
682 | def write_installed_files(self, paths, prefix, dry_run=False):
683 | """
684 | Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any
685 | existing ``RECORD`` file is silently overwritten.
686 |
687 | prefix is used to determine when to write absolute paths.
688 | """
689 | prefix = os.path.join(prefix, '')
690 | base = os.path.dirname(self.path)
691 | base_under_prefix = base.startswith(prefix)
692 | base = os.path.join(base, '')
693 | record_path = self.get_distinfo_file('RECORD')
694 | logger.info('creating %s', record_path)
695 | if dry_run:
696 | return None
697 | with CSVWriter(record_path) as writer:
698 | for path in paths:
699 | if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')):
700 | # do not put size and hash, as in PEP-376
701 | hash_value = size = ''
702 | else:
703 | size = '%d' % os.path.getsize(path)
704 | with open(path, 'rb') as fp:
705 | hash_value = self.get_hash(fp.read())
706 | if path.startswith(base) or (base_under_prefix
707 | and path.startswith(prefix)):
708 | path = os.path.relpath(path, base)
709 | writer.writerow((path, hash_value, size))
710 |
711 | # add the RECORD file itself
712 | if record_path.startswith(base):
713 | record_path = os.path.relpath(record_path, base)
714 | writer.writerow((record_path, '', ''))
715 | return record_path
716 |
717 | def check_installed_files(self):
718 | """
719 | Checks that the hashes and sizes of the files in ``RECORD`` are
720 | matched by the files themselves. Returns a (possibly empty) list of
721 | mismatches. Each entry in the mismatch list will be a tuple consisting
722 | of the path, 'exists', 'size' or 'hash' according to what didn't match
723 | (existence is checked first, then size, then hash), the expected
724 | value and the actual value.
725 | """
726 | mismatches = []
727 | base = os.path.dirname(self.path)
728 | record_path = self.get_distinfo_file('RECORD')
729 | for path, hash_value, size in self.list_installed_files():
730 | if not os.path.isabs(path):
731 | path = os.path.join(base, path)
732 | if path == record_path:
733 | continue
734 | if not os.path.exists(path):
735 | mismatches.append((path, 'exists', True, False))
736 | elif os.path.isfile(path):
737 | actual_size = str(os.path.getsize(path))
738 | if size and actual_size != size:
739 | mismatches.append((path, 'size', size, actual_size))
740 | elif hash_value:
741 | if '=' in hash_value:
742 | hasher = hash_value.split('=', 1)[0]
743 | else:
744 | hasher = None
745 |
746 | with open(path, 'rb') as f:
747 | actual_hash = self.get_hash(f.read(), hasher)
748 | if actual_hash != hash_value:
749 | mismatches.append(
750 | (path, 'hash', hash_value, actual_hash))
751 | return mismatches
752 |
753 | @cached_property
754 | def shared_locations(self):
755 | """
756 | A dictionary of shared locations whose keys are in the set 'prefix',
757 | 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'.
758 | The corresponding value is the absolute path of that category for
759 | this distribution, and takes into account any paths selected by the
760 | user at installation time (e.g. via command-line arguments). In the
761 | case of the 'namespace' key, this would be a list of absolute paths
762 | for the roots of namespace packages in this distribution.
763 |
764 | The first time this property is accessed, the relevant information is
765 | read from the SHARED file in the .dist-info directory.
766 | """
767 | result = {}
768 | shared_path = os.path.join(self.path, 'SHARED')
769 | if os.path.isfile(shared_path):
770 | with codecs.open(shared_path, 'r', encoding='utf-8') as f:
771 | lines = f.read().splitlines()
772 | for line in lines:
773 | key, value = line.split('=', 1)
774 | if key == 'namespace':
775 | result.setdefault(key, []).append(value)
776 | else:
777 | result[key] = value
778 | return result
779 |
780 | def write_shared_locations(self, paths, dry_run=False):
781 | """
782 | Write shared location information to the SHARED file in .dist-info.
783 | :param paths: A dictionary as described in the documentation for
784 | :meth:`shared_locations`.
785 | :param dry_run: If True, the action is logged but no file is actually
786 | written.
787 | :return: The path of the file written to.
788 | """
789 | shared_path = os.path.join(self.path, 'SHARED')
790 | logger.info('creating %s', shared_path)
791 | if dry_run:
792 | return None
793 | lines = []
794 | for key in ('prefix', 'lib', 'headers', 'scripts', 'data'):
795 | path = paths[key]
796 | if os.path.isdir(paths[key]):
797 | lines.append('%s=%s' % (key, path))
798 | for ns in paths.get('namespace', ()):
799 | lines.append('namespace=%s' % ns)
800 |
801 | with codecs.open(shared_path, 'w', encoding='utf-8') as f:
802 | f.write('\n'.join(lines))
803 | return shared_path
804 |
805 | def get_distinfo_resource(self, path):
806 | if path not in DIST_FILES:
807 | raise DistlibException('invalid path for a dist-info file: '
808 | '%r at %r' % (path, self.path))
809 | finder = resources.finder_for_path(self.path)
810 | if finder is None:
811 | raise DistlibException('Unable to get a finder for %s' % self.path)
812 | return finder.find(path)
813 |
814 | def get_distinfo_file(self, path):
815 | """
816 | Returns a path located under the ``.dist-info`` directory. Returns a
817 | string representing the path.
818 |
819 | :parameter path: a ``'/'``-separated path relative to the
820 | ``.dist-info`` directory or an absolute path;
821 | If *path* is an absolute path and doesn't start
822 | with the ``.dist-info`` directory path,
823 | a :class:`DistlibException` is raised
824 | :type path: str
825 | :rtype: str
826 | """
827 | # Check if it is an absolute path # XXX use relpath, add tests
828 | if path.find(os.sep) >= 0:
829 | # it's an absolute path?
830 | distinfo_dirname, path = path.split(os.sep)[-2:]
831 | if distinfo_dirname != self.path.split(os.sep)[-1]:
832 | raise DistlibException(
833 | 'dist-info file %r does not belong to the %r %s '
834 | 'distribution' % (path, self.name, self.version))
835 |
836 | # The file must be relative
837 | if path not in DIST_FILES:
838 | raise DistlibException('invalid path for a dist-info file: '
839 | '%r at %r' % (path, self.path))
840 |
841 | return os.path.join(self.path, path)
842 |
843 | def list_distinfo_files(self):
844 | """
845 | Iterates over the ``RECORD`` entries and returns paths for each line if
846 | the path is pointing to a file located in the ``.dist-info`` directory
847 | or one of its subdirectories.
848 |
849 | :returns: iterator of paths
850 | """
851 | base = os.path.dirname(self.path)
852 | for path, checksum, size in self._get_records():
853 | # XXX add separator or use real relpath algo
854 | if not os.path.isabs(path):
855 | path = os.path.join(base, path)
856 | if path.startswith(self.path):
857 | yield path
858 |
859 | def __eq__(self, other):
860 | return (isinstance(other, InstalledDistribution)
861 | and self.path == other.path)
862 |
863 | # See http://docs.python.org/reference/datamodel#object.__hash__
864 | __hash__ = object.__hash__
865 |
866 |
867 | class EggInfoDistribution(BaseInstalledDistribution):
868 | """Created with the *path* of the ``.egg-info`` directory or file provided
869 | to the constructor. It reads the metadata contained in the file itself, or
870 | if the given path happens to be a directory, the metadata is read from the
871 | file ``PKG-INFO`` under that directory."""
872 |
873 | requested = True # as we have no way of knowing, assume it was
874 | shared_locations = {}
875 |
876 | def __init__(self, path, env=None):
877 |
878 | def set_name_and_version(s, n, v):
879 | s.name = n
880 | s.key = n.lower() # for case-insensitive comparisons
881 | s.version = v
882 |
883 | self.path = path
884 | self.dist_path = env
885 | if env and env._cache_enabled and path in env._cache_egg.path:
886 | metadata = env._cache_egg.path[path].metadata
887 | set_name_and_version(self, metadata.name, metadata.version)
888 | else:
889 | metadata = self._get_metadata(path)
890 |
891 | # Need to be set before caching
892 | set_name_and_version(self, metadata.name, metadata.version)
893 |
894 | if env and env._cache_enabled:
895 | env._cache_egg.add(self)
896 | super(EggInfoDistribution, self).__init__(metadata, path, env)
897 |
898 | def _get_metadata(self, path):
899 | requires = None
900 |
901 | def parse_requires_data(data):
902 | """Create a list of dependencies from a requires.txt file.
903 |
904 | *data*: the contents of a setuptools-produced requires.txt file.
905 | """
906 | reqs = []
907 | lines = data.splitlines()
908 | for line in lines:
909 | line = line.strip()
910 | # sectioned files have bare newlines (separating sections)
911 | if not line: # pragma: no cover
912 | continue
913 | if line.startswith('['): # pragma: no cover
914 | logger.warning(
915 | 'Unexpected line: quitting requirement scan: %r', line)
916 | break
917 | r = parse_requirement(line)
918 | if not r: # pragma: no cover
919 | logger.warning('Not recognised as a requirement: %r', line)
920 | continue
921 | if r.extras: # pragma: no cover
922 | logger.warning('extra requirements in requires.txt are '
923 | 'not supported')
924 | if not r.constraints:
925 | reqs.append(r.name)
926 | else:
927 | cons = ', '.join('%s%s' % c for c in r.constraints)
928 | reqs.append('%s (%s)' % (r.name, cons))
929 | return reqs
930 |
931 | def parse_requires_path(req_path):
932 | """Create a list of dependencies from a requires.txt file.
933 |
934 | *req_path*: the path to a setuptools-produced requires.txt file.
935 | """
936 |
937 | reqs = []
938 | try:
939 | with codecs.open(req_path, 'r', 'utf-8') as fp:
940 | reqs = parse_requires_data(fp.read())
941 | except IOError:
942 | pass
943 | return reqs
944 |
945 | tl_path = tl_data = None
946 | if path.endswith('.egg'):
947 | if os.path.isdir(path):
948 | p = os.path.join(path, 'EGG-INFO')
949 | meta_path = os.path.join(p, 'PKG-INFO')
950 | metadata = Metadata(path=meta_path, scheme='legacy')
951 | req_path = os.path.join(p, 'requires.txt')
952 | tl_path = os.path.join(p, 'top_level.txt')
953 | requires = parse_requires_path(req_path)
954 | else:
955 | # FIXME handle the case where zipfile is not available
956 | zipf = zipimport.zipimporter(path)
957 | fileobj = StringIO(
958 | zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'))
959 | metadata = Metadata(fileobj=fileobj, scheme='legacy')
960 | try:
961 | data = zipf.get_data('EGG-INFO/requires.txt')
962 | tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode(
963 | 'utf-8')
964 | requires = parse_requires_data(data.decode('utf-8'))
965 | except IOError:
966 | requires = None
967 | elif path.endswith('.egg-info'):
968 | if os.path.isdir(path):
969 | req_path = os.path.join(path, 'requires.txt')
970 | requires = parse_requires_path(req_path)
971 | path = os.path.join(path, 'PKG-INFO')
972 | tl_path = os.path.join(path, 'top_level.txt')
973 | metadata = Metadata(path=path, scheme='legacy')
974 | else:
975 | raise DistlibException('path must end with .egg-info or .egg, '
976 | 'got %r' % path)
977 |
978 | if requires:
979 | metadata.add_requirements(requires)
980 | # look for top-level modules in top_level.txt, if present
981 | if tl_data is None:
982 | if tl_path is not None and os.path.exists(tl_path):
983 | with open(tl_path, 'rb') as f:
984 | tl_data = f.read().decode('utf-8')
985 | if not tl_data:
986 | tl_data = []
987 | else:
988 | tl_data = tl_data.splitlines()
989 | self.modules = tl_data
990 | return metadata
991 |
992 | def __repr__(self):
993 | return '<EggInfoDistribution %r %s at %r>' % (self.name, self.version,
994 | self.path)
995 |
996 | def __str__(self):
997 | return "%s %s" % (self.name, self.version)
998 |
999 | def check_installed_files(self):
1000 | """
1001 | Checks that the hashes and sizes of the files in ``RECORD`` are
1002 | matched by the files themselves. Returns a (possibly empty) list of
1003 | mismatches. Each entry in the mismatch list will be a tuple consisting
1004 | of the path, 'exists', 'size' or 'hash' according to what didn't match
1005 | (existence is checked first, then size, then hash), the expected
1006 | value and the actual value.
1007 | """
1008 | mismatches = []
1009 | record_path = os.path.join(self.path, 'installed-files.txt')
1010 | if os.path.exists(record_path):
1011 | for path, _, _ in self.list_installed_files():
1012 | if path == record_path:
1013 | continue
1014 | if not os.path.exists(path):
1015 | mismatches.append((path, 'exists', True, False))
1016 | return mismatches
1017 |
1018 | def list_installed_files(self):
1019 | """
1020 | Iterates over the ``installed-files.txt`` entries and returns a tuple
1021 | ``(path, hash, size)`` for each line.
1022 |
1023 | :returns: a list of (path, hash, size)
1024 | """
1025 |
1026 | def _md5(path):
1027 | f = open(path, 'rb')
1028 | try:
1029 | content = f.read()
1030 | finally:
1031 | f.close()
1032 | return hashlib.md5(content).hexdigest()
1033 |
1034 | def _size(path):
1035 | return os.stat(path).st_size
1036 |
1037 | record_path = os.path.join(self.path, 'installed-files.txt')
1038 | result = []
1039 | if os.path.exists(record_path):
1040 | with codecs.open(record_path, 'r', encoding='utf-8') as f:
1041 | for line in f:
1042 | line = line.strip()
1043 | p = os.path.normpath(os.path.join(self.path, line))
1044 | # "./" is present as a marker between installed files
1045 | # and installation metadata files
1046 | if not os.path.exists(p):
1047 | logger.warning('Non-existent file: %s', p)
1048 | if p.endswith(('.pyc', '.pyo')):
1049 | continue
1050 | # otherwise fall through and fail
1051 | if not os.path.isdir(p):
1052 | result.append((p, _md5(p), _size(p)))
1053 | result.append((record_path, None, None))
1054 | return result
1055 |
1056 | def list_distinfo_files(self, absolute=False):
1057 | """
1058 | Iterates over the ``installed-files.txt`` entries and returns paths for
1059 | each line if the path is pointing to a file located in the
1060 | ``.egg-info`` directory or one of its subdirectories.
1061 |
1062 | :parameter absolute: If *absolute* is ``True``, each returned path is
1063 | transformed into a local absolute path. Otherwise the
1064 | raw value from ``installed-files.txt`` is returned.
1065 | :type absolute: boolean
1066 | :returns: iterator of paths
1067 | """
1068 | record_path = os.path.join(self.path, 'installed-files.txt')
1069 | if os.path.exists(record_path):
1070 | skip = True
1071 | with codecs.open(record_path, 'r', encoding='utf-8') as f:
1072 | for line in f:
1073 | line = line.strip()
1074 | if line == './':
1075 | skip = False
1076 | continue
1077 | if not skip:
1078 | p = os.path.normpath(os.path.join(self.path, line))
1079 | if p.startswith(self.path):
1080 | if absolute:
1081 | yield p
1082 | else:
1083 | yield line
1084 |
1085 | def __eq__(self, other):
1086 | return (isinstance(other, EggInfoDistribution)
1087 | and self.path == other.path)
1088 |
1089 | # See http://docs.python.org/reference/datamodel#object.__hash__
1090 | __hash__ = object.__hash__
1091 |
1092 |
1093 | new_dist_class = InstalledDistribution
1094 | old_dist_class = EggInfoDistribution
1095 |
1096 |
1097 | class DependencyGraph(object):
1098 | """
1099 | Represents a dependency graph between distributions.
1100 |
1101 | The dependency relationships are stored in an ``adjacency_list`` that maps
1102 | distributions to a list of ``(other, label)`` tuples where ``other``
1103 | is a distribution and the edge is labeled with ``label`` (i.e. the version
1104 | specifier, if such was provided). Also, for more efficient traversal, for
1105 | every distribution ``x``, a list of predecessors is kept in
1106 | ``reverse_list[x]``. An edge from distribution ``a`` to
1107 | distribution ``b`` means that ``a`` depends on ``b``. If any missing
1108 | dependencies are found, they are stored in ``missing``, which is a
1109 | dictionary that maps distributions to a list of requirements that were not
1110 | provided by any other distributions.
1111 | """
1112 |
1113 | def __init__(self):
1114 | self.adjacency_list = {}
1115 | self.reverse_list = {}
1116 | self.missing = {}
1117 |
1118 | def add_distribution(self, distribution):
1119 | """Add the *distribution* to the graph.
1120 |
1121 | :type distribution: :class:`distutils2.database.InstalledDistribution`
1122 | or :class:`distutils2.database.EggInfoDistribution`
1123 | """
1124 | self.adjacency_list[distribution] = []
1125 | self.reverse_list[distribution] = []
1126 | # self.missing[distribution] = []
1127 |
1128 | def add_edge(self, x, y, label=None):
1129 | """Add an edge from distribution *x* to distribution *y* with the given
1130 | *label*.
1131 |
1132 | :type x: :class:`distutils2.database.InstalledDistribution` or
1133 | :class:`distutils2.database.EggInfoDistribution`
1134 | :type y: :class:`distutils2.database.InstalledDistribution` or
1135 | :class:`distutils2.database.EggInfoDistribution`
1136 | :type label: ``str`` or ``None``
1137 | """
1138 | self.adjacency_list[x].append((y, label))
1139 | # multiple edges are allowed, so be careful
1140 | if x not in self.reverse_list[y]:
1141 | self.reverse_list[y].append(x)
1142 |
1143 | def add_missing(self, distribution, requirement):
1144 | """
1145 | Add a missing *requirement* for the given *distribution*.
1146 |
1147 | :type distribution: :class:`distutils2.database.InstalledDistribution`
1148 | or :class:`distutils2.database.EggInfoDistribution`
1149 | :type requirement: ``str``
1150 | """
1151 | logger.debug('%s missing %r', distribution, requirement)
1152 | self.missing.setdefault(distribution, []).append(requirement)
1153 |
1154 | def _repr_dist(self, dist):
1155 | return '%s %s' % (dist.name, dist.version)
1156 |
1157 | def repr_node(self, dist, level=1):
1158 | """Prints only a subgraph"""
1159 | output = [self._repr_dist(dist)]
1160 | for other, label in self.adjacency_list[dist]:
1161 | dist = self._repr_dist(other)
1162 | if label is not None:
1163 | dist = '%s [%s]' % (dist, label)
1164 | output.append(' ' * level + str(dist))
1165 | suboutput = self.repr_node(other, level + 1)
1166 | subs = suboutput.split('\n')
1167 | output.extend(subs[1:])
1168 | return '\n'.join(output)
1169 |
1170 | def to_dot(self, f, skip_disconnected=True):
1171 | """Writes a DOT output for the graph to the provided file *f*.
1172 |
1173 | If *skip_disconnected* is set to ``True``, then all distributions
1174 | that are not dependent on any other distribution are skipped.
1175 |
1176 | :type f: has to support ``file``-like operations
1177 | :type skip_disconnected: ``bool``
1178 | """
1179 | disconnected = []
1180 |
1181 | f.write("digraph dependencies {\n")
1182 | for dist, adjs in self.adjacency_list.items():
1183 | if len(adjs) == 0 and not skip_disconnected:
1184 | disconnected.append(dist)
1185 | for other, label in adjs:
1186 | if label is not None:
1187 | f.write('"%s" -> "%s" [label="%s"]\n' %
1188 | (dist.name, other.name, label))
1189 | else:
1190 | f.write('"%s" -> "%s"\n' % (dist.name, other.name))
1191 | if not skip_disconnected and len(disconnected) > 0:
1192 | f.write('subgraph disconnected {\n')
1193 | f.write('label = "Disconnected"\n')
1194 | f.write('bgcolor = red\n')
1195 |
1196 | for dist in disconnected:
1197 | f.write('"%s"' % dist.name)
1198 | f.write('\n')
1199 | f.write('}\n')
1200 | f.write('}\n')
1201 |
1202 | def topological_sort(self):
1203 | """
1204 | Perform a topological sort of the graph.
1205 | :return: A tuple, the first element of which is a topologically sorted
1206 | list of distributions, and the second element of which is a
1207 | list of distributions that cannot be sorted because they have
1208 | circular dependencies and so form a cycle.
1209 | """
1210 | result = []
1211 | # Make a shallow copy of the adjacency list
1212 | alist = {}
1213 | for k, v in self.adjacency_list.items():
1214 | alist[k] = v[:]
1215 | while True:
1216 | # See what we can remove in this run
1217 | to_remove = []
1218 | for k, v in list(alist.items())[:]:
1219 | if not v:
1220 | to_remove.append(k)
1221 | del alist[k]
1222 | if not to_remove:
1223 | # What's left in alist (if anything) is a cycle.
1224 | break
1225 | # Remove from the adjacency list of others
1226 | for k, v in alist.items():
1227 | alist[k] = [(d, r) for d, r in v if d not in to_remove]
1228 | logger.debug('Moving to result: %s',
1229 | ['%s (%s)' % (d.name, d.version) for d in to_remove])
1230 | result.extend(to_remove)
1231 | return result, list(alist.keys())
1232 |
1233 | def __repr__(self):
1234 | """Representation of the graph"""
1235 | output = []
1236 | for dist, adjs in self.adjacency_list.items():
1237 | output.append(self.repr_node(dist))
1238 | return '\n'.join(output)
1239 |
1240 |
1241 | def make_graph(dists, scheme='default'):
1242 | """Makes a dependency graph from the given distributions.
1243 |
1244 | :parameter dists: a list of distributions
1245 | :type dists: list of :class:`distutils2.database.InstalledDistribution` and
1246 | :class:`distutils2.database.EggInfoDistribution` instances
1247 | :rtype: a :class:`DependencyGraph` instance
1248 | """
1249 | scheme = get_scheme(scheme)
1250 | graph = DependencyGraph()
1251 | provided = {} # maps names to lists of (version, dist) tuples
1252 |
1253 | # first, build the graph and find out what's provided
1254 | for dist in dists:
1255 | graph.add_distribution(dist)
1256 |
1257 | for p in dist.provides:
1258 | name, version = parse_name_and_version(p)
1259 | logger.debug('Add to provided: %s, %s, %s', name, version, dist)
1260 | provided.setdefault(name, []).append((version, dist))
1261 |
1262 | # now make the edges
1263 | for dist in dists:
1264 | requires = (dist.run_requires | dist.meta_requires
1265 | | dist.build_requires | dist.dev_requires)
1266 | for req in requires:
1267 | try:
1268 | matcher = scheme.matcher(req)
1269 | except UnsupportedVersionError:
1270 | # XXX compat-mode if cannot read the version
1271 | logger.warning('could not read version %r - using name only',
1272 | req)
1273 | name = req.split()[0]
1274 | matcher = scheme.matcher(name)
1275 |
1276 | name = matcher.key # case-insensitive
1277 |
1278 | matched = False
1279 | if name in provided:
1280 | for version, provider in provided[name]:
1281 | try:
1282 | match = matcher.match(version)
1283 | except UnsupportedVersionError:
1284 | match = False
1285 |
1286 | if match:
1287 | graph.add_edge(dist, provider, req)
1288 | matched = True
1289 | break
1290 | if not matched:
1291 | graph.add_missing(dist, req)
1292 | return graph
1293 |
1294 |
1295 | def get_dependent_dists(dists, dist):
1296 | """Recursively generate a list of distributions from *dists* that are
1297 | dependent on *dist*.
1298 |
1299 | :param dists: a list of distributions
1300 | :param dist: a distribution, member of *dists* for which we are interested
1301 | """
1302 | if dist not in dists:
1303 | raise DistlibException('given distribution %r is not a member '
1304 | 'of the list' % dist.name)
1305 | graph = make_graph(dists)
1306 |
1307 | dep = [dist] # dependent distributions
1308 | todo = graph.reverse_list[dist] # list of nodes we should inspect
1309 |
1310 | while todo:
1311 | d = todo.pop()
1312 | dep.append(d)
1313 | for succ in graph.reverse_list[d]:
1314 | if succ not in dep:
1315 | todo.append(succ)
1316 |
1317 | dep.pop(0) # remove dist from dep, was there to prevent infinite loops
1318 | return dep
1319 |
1320 |
1321 | def get_required_dists(dists, dist):
1322 | """Recursively generate a list of distributions from *dists* that are
1323 | required by *dist*.
1324 |
1325 | :param dists: a list of distributions
1326 | :param dist: a distribution, member of *dists* for which we are interested
1327 | in finding the dependencies.
1328 | """
1329 | if dist not in dists:
1330 | raise DistlibException('given distribution %r is not a member '
1331 | 'of the list' % dist.name)
1332 | graph = make_graph(dists)
1333 |
1334 | req = set() # required distributions
1335 | todo = graph.adjacency_list[dist] # list of nodes we should inspect
1336 | seen = set(t[0] for t in todo) # already added to todo
1337 |
1338 | while todo:
1339 | d = todo.pop()[0]
1340 | req.add(d)
1341 | pred_list = graph.adjacency_list[d]
1342 | for pred in pred_list:
1343 | d = pred[0]
1344 | if d not in req and d not in seen:
1345 | seen.add(d)
1346 | todo.append(pred)
1347 | return req
1348 |
1349 |
1350 | def make_dist(name, version, **kwargs):
1351 | """
1352 | A convenience method for making a dist given just a name and version.
1353 | """
1354 | summary = kwargs.pop('summary', 'Placeholder for summary')
1355 | md = Metadata(**kwargs)
1356 | md.name = name
1357 | md.version = version
1358 | md.summary = summary or 'Placeholder for summary'
1359 | return Distribution(md)
1360 |
```