This is page 54 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/werkzeug/formparser.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import typing as t
4 | from io import BytesIO
5 | from urllib.parse import parse_qsl
6 |
7 | from ._internal import _plain_int
8 | from .datastructures import FileStorage
9 | from .datastructures import Headers
10 | from .datastructures import MultiDict
11 | from .exceptions import RequestEntityTooLarge
12 | from .http import parse_options_header
13 | from .sansio.multipart import Data
14 | from .sansio.multipart import Epilogue
15 | from .sansio.multipart import Field
16 | from .sansio.multipart import File
17 | from .sansio.multipart import MultipartDecoder
18 | from .sansio.multipart import NeedData
19 | from .wsgi import get_content_length
20 | from .wsgi import get_input_stream
21 |
22 | # there are some platforms where SpooledTemporaryFile is not available.
23 | # In that case we need to provide a fallback.
24 | try:
25 | from tempfile import SpooledTemporaryFile
26 | except ImportError:
27 | from tempfile import TemporaryFile
28 |
29 | SpooledTemporaryFile = None # type: ignore
30 |
31 | if t.TYPE_CHECKING:
32 | import typing as te
33 |
34 | from _typeshed.wsgi import WSGIEnvironment
35 |
36 | t_parse_result = t.Tuple[
37 | t.IO[bytes], MultiDict[str, str], MultiDict[str, FileStorage]
38 | ]
39 |
40 | class TStreamFactory(te.Protocol):
41 | def __call__(
42 | self,
43 | total_content_length: int | None,
44 | content_type: str | None,
45 | filename: str | None,
46 | content_length: int | None = None,
47 | ) -> t.IO[bytes]: ...
48 |
49 |
50 | F = t.TypeVar("F", bound=t.Callable[..., t.Any])
51 |
52 |
53 | def default_stream_factory(
54 | total_content_length: int | None,
55 | content_type: str | None,
56 | filename: str | None,
57 | content_length: int | None = None,
58 | ) -> t.IO[bytes]:
59 | max_size = 1024 * 500
60 |
61 | if SpooledTemporaryFile is not None:
62 | return t.cast(t.IO[bytes], SpooledTemporaryFile(max_size=max_size, mode="rb+"))
63 | elif total_content_length is None or total_content_length > max_size:
64 | return t.cast(t.IO[bytes], TemporaryFile("rb+"))
65 |
66 | return BytesIO()
67 |
68 |
69 | def parse_form_data(
70 | environ: WSGIEnvironment,
71 | stream_factory: TStreamFactory | None = None,
72 | max_form_memory_size: int | None = None,
73 | max_content_length: int | None = None,
74 | cls: type[MultiDict[str, t.Any]] | None = None,
75 | silent: bool = True,
76 | *,
77 | max_form_parts: int | None = None,
78 | ) -> t_parse_result:
79 | """Parse the form data in the environ and return it as tuple in the form
80 | ``(stream, form, files)``. You should only call this method if the
81 | transport method is `POST`, `PUT`, or `PATCH`.
82 |
83 | If the mimetype of the data transmitted is `multipart/form-data` the
84 | files multidict will be filled with `FileStorage` objects. If the
85 | mimetype is unknown the input stream is wrapped and returned as first
86 | argument, else the stream is empty.
87 |
88 | This is a shortcut for the common usage of :class:`FormDataParser`.
89 |
90 | :param environ: the WSGI environment to be used for parsing.
91 | :param stream_factory: An optional callable that returns a new read and
92 | writeable file descriptor. This callable works
93 | the same as :meth:`Response._get_file_stream`.
94 | :param max_form_memory_size: the maximum number of bytes to be accepted for
95 | in-memory stored form data. If the data
96 | exceeds the value specified an
97 | :exc:`~exceptions.RequestEntityTooLarge`
98 | exception is raised.
99 | :param max_content_length: If this is provided and the transmitted data
100 | is longer than this value an
101 | :exc:`~exceptions.RequestEntityTooLarge`
102 | exception is raised.
103 | :param cls: an optional dict class to use. If this is not specified
104 | or `None` the default :class:`MultiDict` is used.
105 | :param silent: If set to False parsing errors will not be caught.
106 | :param max_form_parts: The maximum number of multipart parts to be parsed. If this
107 | is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
108 | :return: A tuple in the form ``(stream, form, files)``.
109 |
110 | .. versionchanged:: 3.0
111 | The ``charset`` and ``errors`` parameters were removed.
112 |
113 | .. versionchanged:: 2.3
114 | Added the ``max_form_parts`` parameter.
115 |
116 | .. versionadded:: 0.5.1
117 | Added the ``silent`` parameter.
118 |
119 | .. versionadded:: 0.5
120 | Added the ``max_form_memory_size``, ``max_content_length``, and ``cls``
121 | parameters.
122 | """
123 | return FormDataParser(
124 | stream_factory=stream_factory,
125 | max_form_memory_size=max_form_memory_size,
126 | max_content_length=max_content_length,
127 | max_form_parts=max_form_parts,
128 | silent=silent,
129 | cls=cls,
130 | ).parse_from_environ(environ)
131 |
132 |
133 | class FormDataParser:
134 | """This class implements parsing of form data for Werkzeug. By itself
135 | it can parse multipart and url encoded form data. It can be subclassed
136 | and extended but for most mimetypes it is a better idea to use the
137 | untouched stream and expose it as separate attributes on a request
138 | object.
139 |
140 | :param stream_factory: An optional callable that returns a new read and
141 | writeable file descriptor. This callable works
142 | the same as :meth:`Response._get_file_stream`.
143 | :param max_form_memory_size: the maximum number of bytes to be accepted for
144 | in-memory stored form data. If the data
145 | exceeds the value specified an
146 | :exc:`~exceptions.RequestEntityTooLarge`
147 | exception is raised.
148 | :param max_content_length: If this is provided and the transmitted data
149 | is longer than this value an
150 | :exc:`~exceptions.RequestEntityTooLarge`
151 | exception is raised.
152 | :param cls: an optional dict class to use. If this is not specified
153 | or `None` the default :class:`MultiDict` is used.
154 | :param silent: If set to False parsing errors will not be caught.
155 | :param max_form_parts: The maximum number of multipart parts to be parsed. If this
156 | is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
157 |
158 | .. versionchanged:: 3.0
159 | The ``charset`` and ``errors`` parameters were removed.
160 |
161 | .. versionchanged:: 3.0
162 | The ``parse_functions`` attribute and ``get_parse_func`` methods were removed.
163 |
164 | .. versionchanged:: 2.2.3
165 | Added the ``max_form_parts`` parameter.
166 |
167 | .. versionadded:: 0.8
168 | """
169 |
170 | def __init__(
171 | self,
172 | stream_factory: TStreamFactory | None = None,
173 | max_form_memory_size: int | None = None,
174 | max_content_length: int | None = None,
175 | cls: type[MultiDict[str, t.Any]] | None = None,
176 | silent: bool = True,
177 | *,
178 | max_form_parts: int | None = None,
179 | ) -> None:
180 | if stream_factory is None:
181 | stream_factory = default_stream_factory
182 |
183 | self.stream_factory = stream_factory
184 | self.max_form_memory_size = max_form_memory_size
185 | self.max_content_length = max_content_length
186 | self.max_form_parts = max_form_parts
187 |
188 | if cls is None:
189 | cls = t.cast("type[MultiDict[str, t.Any]]", MultiDict)
190 |
191 | self.cls = cls
192 | self.silent = silent
193 |
194 | def parse_from_environ(self, environ: WSGIEnvironment) -> t_parse_result:
195 | """Parses the information from the environment as form data.
196 |
197 | :param environ: the WSGI environment to be used for parsing.
198 | :return: A tuple in the form ``(stream, form, files)``.
199 | """
200 | stream = get_input_stream(environ, max_content_length=self.max_content_length)
201 | content_length = get_content_length(environ)
202 | mimetype, options = parse_options_header(environ.get("CONTENT_TYPE"))
203 | return self.parse(
204 | stream,
205 | content_length=content_length,
206 | mimetype=mimetype,
207 | options=options,
208 | )
209 |
210 | def parse(
211 | self,
212 | stream: t.IO[bytes],
213 | mimetype: str,
214 | content_length: int | None,
215 | options: dict[str, str] | None = None,
216 | ) -> t_parse_result:
217 | """Parses the information from the given stream, mimetype,
218 | content length and mimetype parameters.
219 |
220 | :param stream: an input stream
221 | :param mimetype: the mimetype of the data
222 | :param content_length: the content length of the incoming data
223 | :param options: optional mimetype parameters (used for
224 | the multipart boundary for instance)
225 | :return: A tuple in the form ``(stream, form, files)``.
226 |
227 | .. versionchanged:: 3.0
228 | The invalid ``application/x-url-encoded`` content type is not
229 | treated as ``application/x-www-form-urlencoded``.
230 | """
231 | if mimetype == "multipart/form-data":
232 | parse_func = self._parse_multipart
233 | elif mimetype == "application/x-www-form-urlencoded":
234 | parse_func = self._parse_urlencoded
235 | else:
236 | return stream, self.cls(), self.cls()
237 |
238 | if options is None:
239 | options = {}
240 |
241 | try:
242 | return parse_func(stream, mimetype, content_length, options)
243 | except ValueError:
244 | if not self.silent:
245 | raise
246 |
247 | return stream, self.cls(), self.cls()
248 |
249 | def _parse_multipart(
250 | self,
251 | stream: t.IO[bytes],
252 | mimetype: str,
253 | content_length: int | None,
254 | options: dict[str, str],
255 | ) -> t_parse_result:
256 | parser = MultiPartParser(
257 | stream_factory=self.stream_factory,
258 | max_form_memory_size=self.max_form_memory_size,
259 | max_form_parts=self.max_form_parts,
260 | cls=self.cls,
261 | )
262 | boundary = options.get("boundary", "").encode("ascii")
263 |
264 | if not boundary:
265 | raise ValueError("Missing boundary")
266 |
267 | form, files = parser.parse(stream, boundary, content_length)
268 | return stream, form, files
269 |
270 | def _parse_urlencoded(
271 | self,
272 | stream: t.IO[bytes],
273 | mimetype: str,
274 | content_length: int | None,
275 | options: dict[str, str],
276 | ) -> t_parse_result:
277 | if (
278 | self.max_form_memory_size is not None
279 | and content_length is not None
280 | and content_length > self.max_form_memory_size
281 | ):
282 | raise RequestEntityTooLarge()
283 |
284 | items = parse_qsl(
285 | stream.read().decode(),
286 | keep_blank_values=True,
287 | errors="werkzeug.url_quote",
288 | )
289 | return stream, self.cls(items), self.cls()
290 |
291 |
292 | class MultiPartParser:
293 | def __init__(
294 | self,
295 | stream_factory: TStreamFactory | None = None,
296 | max_form_memory_size: int | None = None,
297 | cls: type[MultiDict[str, t.Any]] | None = None,
298 | buffer_size: int = 64 * 1024,
299 | max_form_parts: int | None = None,
300 | ) -> None:
301 | self.max_form_memory_size = max_form_memory_size
302 | self.max_form_parts = max_form_parts
303 |
304 | if stream_factory is None:
305 | stream_factory = default_stream_factory
306 |
307 | self.stream_factory = stream_factory
308 |
309 | if cls is None:
310 | cls = t.cast("type[MultiDict[str, t.Any]]", MultiDict)
311 |
312 | self.cls = cls
313 | self.buffer_size = buffer_size
314 |
315 | def fail(self, message: str) -> te.NoReturn:
316 | raise ValueError(message)
317 |
318 | def get_part_charset(self, headers: Headers) -> str:
319 | # Figure out input charset for current part
320 | content_type = headers.get("content-type")
321 |
322 | if content_type:
323 | parameters = parse_options_header(content_type)[1]
324 | ct_charset = parameters.get("charset", "").lower()
325 |
326 | # A safe list of encodings. Modern clients should only send ASCII or UTF-8.
327 | # This list will not be extended further.
328 | if ct_charset in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}:
329 | return ct_charset
330 |
331 | return "utf-8"
332 |
333 | def start_file_streaming(
334 | self, event: File, total_content_length: int | None
335 | ) -> t.IO[bytes]:
336 | content_type = event.headers.get("content-type")
337 |
338 | try:
339 | content_length = _plain_int(event.headers["content-length"])
340 | except (KeyError, ValueError):
341 | content_length = 0
342 |
343 | container = self.stream_factory(
344 | total_content_length=total_content_length,
345 | filename=event.filename,
346 | content_type=content_type,
347 | content_length=content_length,
348 | )
349 | return container
350 |
351 | def parse(
352 | self, stream: t.IO[bytes], boundary: bytes, content_length: int | None
353 | ) -> tuple[MultiDict[str, str], MultiDict[str, FileStorage]]:
354 | current_part: Field | File
355 | container: t.IO[bytes] | list[bytes]
356 | _write: t.Callable[[bytes], t.Any]
357 |
358 | parser = MultipartDecoder(
359 | boundary,
360 | max_form_memory_size=self.max_form_memory_size,
361 | max_parts=self.max_form_parts,
362 | )
363 |
364 | fields = []
365 | files = []
366 |
367 | for data in _chunk_iter(stream.read, self.buffer_size):
368 | parser.receive_data(data)
369 | event = parser.next_event()
370 | while not isinstance(event, (Epilogue, NeedData)):
371 | if isinstance(event, Field):
372 | current_part = event
373 | container = []
374 | _write = container.append
375 | elif isinstance(event, File):
376 | current_part = event
377 | container = self.start_file_streaming(event, content_length)
378 | _write = container.write
379 | elif isinstance(event, Data):
380 | _write(event.data)
381 | if not event.more_data:
382 | if isinstance(current_part, Field):
383 | value = b"".join(container).decode(
384 | self.get_part_charset(current_part.headers), "replace"
385 | )
386 | fields.append((current_part.name, value))
387 | else:
388 | container = t.cast(t.IO[bytes], container)
389 | container.seek(0)
390 | files.append(
391 | (
392 | current_part.name,
393 | FileStorage(
394 | container,
395 | current_part.filename,
396 | current_part.name,
397 | headers=current_part.headers,
398 | ),
399 | )
400 | )
401 |
402 | event = parser.next_event()
403 |
404 | return self.cls(fields), self.cls(files)
405 |
406 |
407 | def _chunk_iter(read: t.Callable[[int], bytes], size: int) -> t.Iterator[bytes | None]:
408 | """Read data in chunks for multipart/form-data parsing. Stop if no data is read.
409 | Yield ``None`` at the end to signal end of parsing.
410 | """
411 | while True:
412 | data = read(size)
413 |
414 | if not data:
415 | break
416 |
417 | yield data
418 |
419 | yield None
420 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/bs4/tests/test_dammit.py:
--------------------------------------------------------------------------------
```python
1 | # encoding: utf-8
2 | import pytest
3 | import logging
4 | import bs4
5 | from bs4 import BeautifulSoup
6 | from bs4.dammit import (
7 | EntitySubstitution,
8 | EncodingDetector,
9 | UnicodeDammit,
10 | )
11 |
12 | class TestUnicodeDammit(object):
13 | """Standalone tests of UnicodeDammit."""
14 |
15 | def test_unicode_input(self):
16 | markup = "I'm already Unicode! \N{SNOWMAN}"
17 | dammit = UnicodeDammit(markup)
18 | assert dammit.unicode_markup == markup
19 |
20 | @pytest.mark.parametrize(
21 | "smart_quotes_to,expect_converted",
22 | [(None, "\u2018\u2019\u201c\u201d"),
23 | ("xml", "‘’“”"),
24 | ("html", "‘’“”"),
25 | ("ascii", "''" + '""'),
26 | ]
27 | )
28 | def test_smart_quotes_to(self, smart_quotes_to, expect_converted):
29 | """Verify the functionality of the smart_quotes_to argument
30 | to the UnicodeDammit constructor."""
31 | markup = b"<foo>\x91\x92\x93\x94</foo>"
32 | converted = UnicodeDammit(
33 | markup, known_definite_encodings=["windows-1252"],
34 | smart_quotes_to=smart_quotes_to
35 | ).unicode_markup
36 | assert converted == "<foo>{}</foo>".format(expect_converted)
37 |
38 | def test_detect_utf8(self):
39 | utf8 = b"Sacr\xc3\xa9 bleu! \xe2\x98\x83"
40 | dammit = UnicodeDammit(utf8)
41 | assert dammit.original_encoding.lower() == 'utf-8'
42 | assert dammit.unicode_markup == 'Sacr\xe9 bleu! \N{SNOWMAN}'
43 |
44 | def test_convert_hebrew(self):
45 | hebrew = b"\xed\xe5\xec\xf9"
46 | dammit = UnicodeDammit(hebrew, ["iso-8859-8"])
47 | assert dammit.original_encoding.lower() == 'iso-8859-8'
48 | assert dammit.unicode_markup == '\u05dd\u05d5\u05dc\u05e9'
49 |
50 | def test_dont_see_smart_quotes_where_there_are_none(self):
51 | utf_8 = b"\343\202\261\343\203\274\343\202\277\343\202\244 Watch"
52 | dammit = UnicodeDammit(utf_8)
53 | assert dammit.original_encoding.lower() == 'utf-8'
54 | assert dammit.unicode_markup.encode("utf-8") == utf_8
55 |
56 | def test_ignore_inappropriate_codecs(self):
57 | utf8_data = "Räksmörgås".encode("utf-8")
58 | dammit = UnicodeDammit(utf8_data, ["iso-8859-8"])
59 | assert dammit.original_encoding.lower() == 'utf-8'
60 |
61 | def test_ignore_invalid_codecs(self):
62 | utf8_data = "Räksmörgås".encode("utf-8")
63 | for bad_encoding in ['.utf8', '...', 'utF---16.!']:
64 | dammit = UnicodeDammit(utf8_data, [bad_encoding])
65 | assert dammit.original_encoding.lower() == 'utf-8'
66 |
67 | def test_exclude_encodings(self):
68 | # This is UTF-8.
69 | utf8_data = "Räksmörgås".encode("utf-8")
70 |
71 | # But if we exclude UTF-8 from consideration, the guess is
72 | # Windows-1252.
73 | dammit = UnicodeDammit(utf8_data, exclude_encodings=["utf-8"])
74 | assert dammit.original_encoding.lower() == 'windows-1252'
75 |
76 | # And if we exclude that, there is no valid guess at all.
77 | dammit = UnicodeDammit(
78 | utf8_data, exclude_encodings=["utf-8", "windows-1252"])
79 | assert dammit.original_encoding == None
80 |
81 | class TestEncodingDetector(object):
82 |
83 | def test_encoding_detector_replaces_junk_in_encoding_name_with_replacement_character(self):
84 | detected = EncodingDetector(
85 | b'<?xml version="1.0" encoding="UTF-\xdb" ?>')
86 | encodings = list(detected.encodings)
87 | assert 'utf-\N{REPLACEMENT CHARACTER}' in encodings
88 |
89 | def test_detect_html5_style_meta_tag(self):
90 |
91 | for data in (
92 | b'<html><meta charset="euc-jp" /></html>',
93 | b"<html><meta charset='euc-jp' /></html>",
94 | b"<html><meta charset=euc-jp /></html>",
95 | b"<html><meta charset=euc-jp/></html>"):
96 | dammit = UnicodeDammit(data, is_html=True)
97 | assert "euc-jp" == dammit.original_encoding
98 |
99 | def test_last_ditch_entity_replacement(self):
100 | # This is a UTF-8 document that contains bytestrings
101 | # completely incompatible with UTF-8 (ie. encoded with some other
102 | # encoding).
103 | #
104 | # Since there is no consistent encoding for the document,
105 | # Unicode, Dammit will eventually encode the document as UTF-8
106 | # and encode the incompatible characters as REPLACEMENT
107 | # CHARACTER.
108 | #
109 | # If chardet is installed, it will detect that the document
110 | # can be converted into ISO-8859-1 without errors. This happens
111 | # to be the wrong encoding, but it is a consistent encoding, so the
112 | # code we're testing here won't run.
113 | #
114 | # So we temporarily disable chardet if it's present.
115 | doc = b"""\357\273\277<?xml version="1.0" encoding="UTF-8"?>
116 | <html><b>\330\250\330\252\330\261</b>
117 | <i>\310\322\321\220\312\321\355\344</i></html>"""
118 | chardet = bs4.dammit.chardet_dammit
119 | logging.disable(logging.WARNING)
120 | try:
121 | def noop(str):
122 | return None
123 | bs4.dammit.chardet_dammit = noop
124 | dammit = UnicodeDammit(doc)
125 | assert True == dammit.contains_replacement_characters
126 | assert "\ufffd" in dammit.unicode_markup
127 |
128 | soup = BeautifulSoup(doc, "html.parser")
129 | assert soup.contains_replacement_characters
130 | finally:
131 | logging.disable(logging.NOTSET)
132 | bs4.dammit.chardet_dammit = chardet
133 |
134 | def test_byte_order_mark_removed(self):
135 | # A document written in UTF-16LE will have its byte order marker stripped.
136 | data = b'\xff\xfe<\x00a\x00>\x00\xe1\x00\xe9\x00<\x00/\x00a\x00>\x00'
137 | dammit = UnicodeDammit(data)
138 | assert "<a>áé</a>" == dammit.unicode_markup
139 | assert "utf-16le" == dammit.original_encoding
140 |
141 | def test_known_definite_versus_user_encodings(self):
142 | # The known_definite_encodings are used before sniffing the
143 | # byte-order mark; the user_encodings are used afterwards.
144 |
145 | # Here's a document in UTF-16LE.
146 | data = b'\xff\xfe<\x00a\x00>\x00\xe1\x00\xe9\x00<\x00/\x00a\x00>\x00'
147 | dammit = UnicodeDammit(data)
148 |
149 | # We can process it as UTF-16 by passing it in as a known
150 | # definite encoding.
151 | before = UnicodeDammit(data, known_definite_encodings=["utf-16"])
152 | assert "utf-16" == before.original_encoding
153 |
154 | # If we pass UTF-18 as a user encoding, it's not even
155 | # tried--the encoding sniffed from the byte-order mark takes
156 | # precedence.
157 | after = UnicodeDammit(data, user_encodings=["utf-8"])
158 | assert "utf-16le" == after.original_encoding
159 | assert ["utf-16le"] == [x[0] for x in dammit.tried_encodings]
160 |
161 | # Here's a document in ISO-8859-8.
162 | hebrew = b"\xed\xe5\xec\xf9"
163 | dammit = UnicodeDammit(hebrew, known_definite_encodings=["utf-8"],
164 | user_encodings=["iso-8859-8"])
165 |
166 | # The known_definite_encodings don't work, BOM sniffing does
167 | # nothing (it only works for a few UTF encodings), but one of
168 | # the user_encodings does work.
169 | assert "iso-8859-8" == dammit.original_encoding
170 | assert ["utf-8", "iso-8859-8"] == [x[0] for x in dammit.tried_encodings]
171 |
172 | def test_deprecated_override_encodings(self):
173 | # override_encodings is a deprecated alias for
174 | # known_definite_encodings.
175 | hebrew = b"\xed\xe5\xec\xf9"
176 | dammit = UnicodeDammit(
177 | hebrew,
178 | known_definite_encodings=["shift-jis"],
179 | override_encodings=["utf-8"],
180 | user_encodings=["iso-8859-8"],
181 | )
182 | assert "iso-8859-8" == dammit.original_encoding
183 |
184 | # known_definite_encodings and override_encodings were tried
185 | # before user_encodings.
186 | assert ["shift-jis", "utf-8", "iso-8859-8"] == (
187 | [x[0] for x in dammit.tried_encodings]
188 | )
189 |
190 | def test_detwingle(self):
191 | # Here's a UTF8 document.
192 | utf8 = ("\N{SNOWMAN}" * 3).encode("utf8")
193 |
194 | # Here's a Windows-1252 document.
195 | windows_1252 = (
196 | "\N{LEFT DOUBLE QUOTATION MARK}Hi, I like Windows!"
197 | "\N{RIGHT DOUBLE QUOTATION MARK}").encode("windows_1252")
198 |
199 | # Through some unholy alchemy, they've been stuck together.
200 | doc = utf8 + windows_1252 + utf8
201 |
202 | # The document can't be turned into UTF-8:
203 | with pytest.raises(UnicodeDecodeError):
204 | doc.decode("utf8")
205 |
206 | # Unicode, Dammit thinks the whole document is Windows-1252,
207 | # and decodes it into "☃☃☃“Hi, I like Windows!”☃☃☃"
208 |
209 | # But if we run it through fix_embedded_windows_1252, it's fixed:
210 | fixed = UnicodeDammit.detwingle(doc)
211 | assert "☃☃☃“Hi, I like Windows!”☃☃☃" == fixed.decode("utf8")
212 |
213 | def test_detwingle_ignores_multibyte_characters(self):
214 | # Each of these characters has a UTF-8 representation ending
215 | # in \x93. \x93 is a smart quote if interpreted as
216 | # Windows-1252. But our code knows to skip over multibyte
217 | # UTF-8 characters, so they'll survive the process unscathed.
218 | for tricky_unicode_char in (
219 | "\N{LATIN SMALL LIGATURE OE}", # 2-byte char '\xc5\x93'
220 | "\N{LATIN SUBSCRIPT SMALL LETTER X}", # 3-byte char '\xe2\x82\x93'
221 | "\xf0\x90\x90\x93", # This is a CJK character, not sure which one.
222 | ):
223 | input = tricky_unicode_char.encode("utf8")
224 | assert input.endswith(b'\x93')
225 | output = UnicodeDammit.detwingle(input)
226 | assert output == input
227 |
228 | def test_find_declared_encoding(self):
229 | # Test our ability to find a declared encoding inside an
230 | # XML or HTML document.
231 | #
232 | # Even if the document comes in as Unicode, it may be
233 | # interesting to know what encoding was claimed
234 | # originally.
235 |
236 | html_unicode = '<html><head><meta charset="utf-8"></head></html>'
237 | html_bytes = html_unicode.encode("ascii")
238 |
239 | xml_unicode= '<?xml version="1.0" encoding="ISO-8859-1" ?>'
240 | xml_bytes = xml_unicode.encode("ascii")
241 |
242 | m = EncodingDetector.find_declared_encoding
243 | assert m(html_unicode, is_html=False) is None
244 | assert "utf-8" == m(html_unicode, is_html=True)
245 | assert "utf-8" == m(html_bytes, is_html=True)
246 |
247 | assert "iso-8859-1" == m(xml_unicode)
248 | assert "iso-8859-1" == m(xml_bytes)
249 |
250 | # Normally, only the first few kilobytes of a document are checked for
251 | # an encoding.
252 | spacer = b' ' * 5000
253 | assert m(spacer + html_bytes) is None
254 | assert m(spacer + xml_bytes) is None
255 |
256 | # But you can tell find_declared_encoding to search an entire
257 | # HTML document.
258 | assert (
259 | m(spacer + html_bytes, is_html=True, search_entire_document=True)
260 | == "utf-8"
261 | )
262 |
263 | # The XML encoding declaration has to be the very first thing
264 | # in the document. We'll allow whitespace before the document
265 | # starts, but nothing else.
266 | assert m(xml_bytes, search_entire_document=True) == "iso-8859-1"
267 | assert m(b' ' + xml_bytes, search_entire_document=True) == "iso-8859-1"
268 | assert m(b'a' + xml_bytes, search_entire_document=True) is None
269 |
270 |
271 | class TestEntitySubstitution(object):
272 | """Standalone tests of the EntitySubstitution class."""
273 | def setup_method(self):
274 | self.sub = EntitySubstitution
275 |
276 |
277 | @pytest.mark.parametrize(
278 | "original,substituted",
279 | [
280 | # Basic case. Unicode characters corresponding to named
281 | # HTML entites are substituted; others are not.
282 | ("foo\u2200\N{SNOWMAN}\u00f5bar",
283 | "foo∀\N{SNOWMAN}õbar"),
284 |
285 | # MS smart quotes are a common source of frustration, so we
286 | # give them a special test.
287 | ('‘’foo“”', "‘’foo“”"),
288 | ]
289 | )
290 | def test_substitute_html(self, original, substituted):
291 | assert self.sub.substitute_html(original) == substituted
292 |
293 | def test_html5_entity(self):
294 | for entity, u in (
295 | # A few spot checks of our ability to recognize
296 | # special character sequences and convert them
297 | # to named entities.
298 | ('⊧', '\u22a7'),
299 | ('𝔑', '\U0001d511'),
300 | ('≧̸', '\u2267\u0338'),
301 | ('¬', '\xac'),
302 | ('⫬', '\u2aec'),
303 |
304 | # We _could_ convert | to &verbarr;, but we don't, because
305 | # | is an ASCII character.
306 | ('|' '|'),
307 |
308 | # Similarly for the fj ligature, which we could convert to
309 | # fj, but we don't.
310 | ("fj", "fj"),
311 |
312 | # We do convert _these_ ASCII characters to HTML entities,
313 | # because that's required to generate valid HTML.
314 | ('>', '>'),
315 | ('<', '<'),
316 | ('&', '&'),
317 | ):
318 | template = '3 %s 4'
319 | raw = template % u
320 | with_entities = template % entity
321 | assert self.sub.substitute_html(raw) == with_entities
322 |
323 | def test_html5_entity_with_variation_selector(self):
324 | # Some HTML5 entities correspond either to a single-character
325 | # Unicode sequence _or_ to the same character plus U+FE00,
326 | # VARIATION SELECTOR 1. We can handle this.
327 | data = "fjords \u2294 penguins"
328 | markup = "fjords ⊔ penguins"
329 | assert self.sub.substitute_html(data) == markup
330 |
331 | data = "fjords \u2294\ufe00 penguins"
332 | markup = "fjords ⊔︀ penguins"
333 | assert self.sub.substitute_html(data) == markup
334 |
335 | def test_xml_converstion_includes_no_quotes_if_make_quoted_attribute_is_false(self):
336 | s = 'Welcome to "my bar"'
337 | assert self.sub.substitute_xml(s, False) == s
338 |
339 | def test_xml_attribute_quoting_normally_uses_double_quotes(self):
340 | assert self.sub.substitute_xml("Welcome", True) == '"Welcome"'
341 | assert self.sub.substitute_xml("Bob's Bar", True) == '"Bob\'s Bar"'
342 |
343 | def test_xml_attribute_quoting_uses_single_quotes_when_value_contains_double_quotes(self):
344 | s = 'Welcome to "my bar"'
345 | assert self.sub.substitute_xml(s, True) == "'Welcome to \"my bar\"'"
346 |
347 | def test_xml_attribute_quoting_escapes_single_quotes_when_value_contains_both_single_and_double_quotes(self):
348 | s = 'Welcome to "Bob\'s Bar"'
349 | assert self.sub.substitute_xml(s, True) == '"Welcome to "Bob\'s Bar""'
350 |
351 | def test_xml_quotes_arent_escaped_when_value_is_not_being_quoted(self):
352 | quoted = 'Welcome to "Bob\'s Bar"'
353 | assert self.sub.substitute_xml(quoted) == quoted
354 |
355 | def test_xml_quoting_handles_angle_brackets(self):
356 | assert self.sub.substitute_xml("foo<bar>") == "foo<bar>"
357 |
358 | def test_xml_quoting_handles_ampersands(self):
359 | assert self.sub.substitute_xml("AT&T") == "AT&T"
360 |
361 | def test_xml_quoting_including_ampersands_when_they_are_part_of_an_entity(self):
362 | assert self.sub.substitute_xml("ÁT&T") == "&Aacute;T&T"
363 |
364 | def test_xml_quoting_ignoring_ampersands_when_they_are_part_of_an_entity(self):
365 | assert self.sub.substitute_xml_containing_entities("ÁT&T") == "ÁT&T"
366 |
367 | def test_quotes_not_html_substituted(self):
368 | """There's no need to do this except inside attribute values."""
369 | text = 'Bob\'s "bar"'
370 | assert self.sub.substitute_html(text) == text
371 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/itsdangerous/serializer.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import collections.abc as cabc
4 | import json
5 | import typing as t
6 |
7 | from .encoding import want_bytes
8 | from .exc import BadPayload
9 | from .exc import BadSignature
10 | from .signer import _make_keys_list
11 | from .signer import Signer
12 |
13 | if t.TYPE_CHECKING:
14 | import typing_extensions as te
15 |
16 | # This should be either be str or bytes. To avoid having to specify the
17 | # bound type, it falls back to a union if structural matching fails.
18 | _TSerialized = te.TypeVar(
19 | "_TSerialized", bound=t.Union[str, bytes], default=t.Union[str, bytes]
20 | )
21 | else:
22 | # Still available at runtime on Python < 3.13, but without the default.
23 | _TSerialized = t.TypeVar("_TSerialized", bound=t.Union[str, bytes])
24 |
25 |
26 | class _PDataSerializer(t.Protocol[_TSerialized]):
27 | def loads(self, payload: _TSerialized, /) -> t.Any: ...
28 | # A signature with additional arguments is not handled correctly by type
29 | # checkers right now, so an overload is used below for serializers that
30 | # don't match this strict protocol.
31 | def dumps(self, obj: t.Any, /) -> _TSerialized: ...
32 |
33 |
34 | # Use TypeIs once it's available in typing_extensions or 3.13.
35 | def is_text_serializer(
36 | serializer: _PDataSerializer[t.Any],
37 | ) -> te.TypeGuard[_PDataSerializer[str]]:
38 | """Checks whether a serializer generates text or binary."""
39 | return isinstance(serializer.dumps({}), str)
40 |
41 |
42 | class Serializer(t.Generic[_TSerialized]):
43 | """A serializer wraps a :class:`~itsdangerous.signer.Signer` to
44 | enable serializing and securely signing data other than bytes. It
45 | can unsign to verify that the data hasn't been changed.
46 |
47 | The serializer provides :meth:`dumps` and :meth:`loads`, similar to
48 | :mod:`json`, and by default uses :mod:`json` internally to serialize
49 | the data to bytes.
50 |
51 | The secret key should be a random string of ``bytes`` and should not
52 | be saved to code or version control. Different salts should be used
53 | to distinguish signing in different contexts. See :doc:`/concepts`
54 | for information about the security of the secret key and salt.
55 |
56 | :param secret_key: The secret key to sign and verify with. Can be a
57 | list of keys, oldest to newest, to support key rotation.
58 | :param salt: Extra key to combine with ``secret_key`` to distinguish
59 | signatures in different contexts.
60 | :param serializer: An object that provides ``dumps`` and ``loads``
61 | methods for serializing data to a string. Defaults to
62 | :attr:`default_serializer`, which defaults to :mod:`json`.
63 | :param serializer_kwargs: Keyword arguments to pass when calling
64 | ``serializer.dumps``.
65 | :param signer: A ``Signer`` class to instantiate when signing data.
66 | Defaults to :attr:`default_signer`, which defaults to
67 | :class:`~itsdangerous.signer.Signer`.
68 | :param signer_kwargs: Keyword arguments to pass when instantiating
69 | the ``Signer`` class.
70 | :param fallback_signers: List of signer parameters to try when
71 | unsigning with the default signer fails. Each item can be a dict
72 | of ``signer_kwargs``, a ``Signer`` class, or a tuple of
73 | ``(signer, signer_kwargs)``. Defaults to
74 | :attr:`default_fallback_signers`.
75 |
76 | .. versionchanged:: 2.0
77 | Added support for key rotation by passing a list to
78 | ``secret_key``.
79 |
80 | .. versionchanged:: 2.0
81 | Removed the default SHA-512 fallback signer from
82 | ``default_fallback_signers``.
83 |
84 | .. versionchanged:: 1.1
85 | Added support for ``fallback_signers`` and configured a default
86 | SHA-512 fallback. This fallback is for users who used the yanked
87 | 1.0.0 release which defaulted to SHA-512.
88 |
89 | .. versionchanged:: 0.14
90 | The ``signer`` and ``signer_kwargs`` parameters were added to
91 | the constructor.
92 | """
93 |
94 | #: The default serialization module to use to serialize data to a
95 | #: string internally. The default is :mod:`json`, but can be changed
96 | #: to any object that provides ``dumps`` and ``loads`` methods.
97 | default_serializer: _PDataSerializer[t.Any] = json
98 |
99 | #: The default ``Signer`` class to instantiate when signing data.
100 | #: The default is :class:`itsdangerous.signer.Signer`.
101 | default_signer: type[Signer] = Signer
102 |
103 | #: The default fallback signers to try when unsigning fails.
104 | default_fallback_signers: list[
105 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
106 | ] = []
107 |
108 | # Serializer[str] if no data serializer is provided, or if it returns str.
109 | @t.overload
110 | def __init__(
111 | self: Serializer[str],
112 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
113 | salt: str | bytes | None = b"itsdangerous",
114 | serializer: None | _PDataSerializer[str] = None,
115 | serializer_kwargs: dict[str, t.Any] | None = None,
116 | signer: type[Signer] | None = None,
117 | signer_kwargs: dict[str, t.Any] | None = None,
118 | fallback_signers: list[
119 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
120 | ]
121 | | None = None,
122 | ): ...
123 |
124 | # Serializer[bytes] with a bytes data serializer positional argument.
125 | @t.overload
126 | def __init__(
127 | self: Serializer[bytes],
128 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
129 | salt: str | bytes | None,
130 | serializer: _PDataSerializer[bytes],
131 | serializer_kwargs: dict[str, t.Any] | None = None,
132 | signer: type[Signer] | None = None,
133 | signer_kwargs: dict[str, t.Any] | None = None,
134 | fallback_signers: list[
135 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
136 | ]
137 | | None = None,
138 | ): ...
139 |
140 | # Serializer[bytes] with a bytes data serializer keyword argument.
141 | @t.overload
142 | def __init__(
143 | self: Serializer[bytes],
144 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
145 | salt: str | bytes | None = b"itsdangerous",
146 | *,
147 | serializer: _PDataSerializer[bytes],
148 | serializer_kwargs: dict[str, t.Any] | None = None,
149 | signer: type[Signer] | None = None,
150 | signer_kwargs: dict[str, t.Any] | None = None,
151 | fallback_signers: list[
152 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
153 | ]
154 | | None = None,
155 | ): ...
156 |
157 | # Fall back with a positional argument. If the strict signature of
158 | # _PDataSerializer doesn't match, fall back to a union, requiring the user
159 | # to specify the type.
160 | @t.overload
161 | def __init__(
162 | self,
163 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
164 | salt: str | bytes | None,
165 | serializer: t.Any,
166 | serializer_kwargs: dict[str, t.Any] | None = None,
167 | signer: type[Signer] | None = None,
168 | signer_kwargs: dict[str, t.Any] | None = None,
169 | fallback_signers: list[
170 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
171 | ]
172 | | None = None,
173 | ): ...
174 |
175 | # Fall back with a keyword argument.
176 | @t.overload
177 | def __init__(
178 | self,
179 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
180 | salt: str | bytes | None = b"itsdangerous",
181 | *,
182 | serializer: t.Any,
183 | serializer_kwargs: dict[str, t.Any] | None = None,
184 | signer: type[Signer] | None = None,
185 | signer_kwargs: dict[str, t.Any] | None = None,
186 | fallback_signers: list[
187 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
188 | ]
189 | | None = None,
190 | ): ...
191 |
192 | def __init__(
193 | self,
194 | secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes],
195 | salt: str | bytes | None = b"itsdangerous",
196 | serializer: t.Any | None = None,
197 | serializer_kwargs: dict[str, t.Any] | None = None,
198 | signer: type[Signer] | None = None,
199 | signer_kwargs: dict[str, t.Any] | None = None,
200 | fallback_signers: list[
201 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
202 | ]
203 | | None = None,
204 | ):
205 | #: The list of secret keys to try for verifying signatures, from
206 | #: oldest to newest. The newest (last) key is used for signing.
207 | #:
208 | #: This allows a key rotation system to keep a list of allowed
209 | #: keys and remove expired ones.
210 | self.secret_keys: list[bytes] = _make_keys_list(secret_key)
211 |
212 | if salt is not None:
213 | salt = want_bytes(salt)
214 | # if salt is None then the signer's default is used
215 |
216 | self.salt = salt
217 |
218 | if serializer is None:
219 | serializer = self.default_serializer
220 |
221 | self.serializer: _PDataSerializer[_TSerialized] = serializer
222 | self.is_text_serializer: bool = is_text_serializer(serializer)
223 |
224 | if signer is None:
225 | signer = self.default_signer
226 |
227 | self.signer: type[Signer] = signer
228 | self.signer_kwargs: dict[str, t.Any] = signer_kwargs or {}
229 |
230 | if fallback_signers is None:
231 | fallback_signers = list(self.default_fallback_signers)
232 |
233 | self.fallback_signers: list[
234 | dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer]
235 | ] = fallback_signers
236 | self.serializer_kwargs: dict[str, t.Any] = serializer_kwargs or {}
237 |
238 | @property
239 | def secret_key(self) -> bytes:
240 | """The newest (last) entry in the :attr:`secret_keys` list. This
241 | is for compatibility from before key rotation support was added.
242 | """
243 | return self.secret_keys[-1]
244 |
245 | def load_payload(
246 | self, payload: bytes, serializer: _PDataSerializer[t.Any] | None = None
247 | ) -> t.Any:
248 | """Loads the encoded object. This function raises
249 | :class:`.BadPayload` if the payload is not valid. The
250 | ``serializer`` parameter can be used to override the serializer
251 | stored on the class. The encoded ``payload`` should always be
252 | bytes.
253 | """
254 | if serializer is None:
255 | use_serializer = self.serializer
256 | is_text = self.is_text_serializer
257 | else:
258 | use_serializer = serializer
259 | is_text = is_text_serializer(serializer)
260 |
261 | try:
262 | if is_text:
263 | return use_serializer.loads(payload.decode("utf-8")) # type: ignore[arg-type]
264 |
265 | return use_serializer.loads(payload) # type: ignore[arg-type]
266 | except Exception as e:
267 | raise BadPayload(
268 | "Could not load the payload because an exception"
269 | " occurred on unserializing the data.",
270 | original_error=e,
271 | ) from e
272 |
273 | def dump_payload(self, obj: t.Any) -> bytes:
274 | """Dumps the encoded object. The return value is always bytes.
275 | If the internal serializer returns text, the value will be
276 | encoded as UTF-8.
277 | """
278 | return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
279 |
280 | def make_signer(self, salt: str | bytes | None = None) -> Signer:
281 | """Creates a new instance of the signer to be used. The default
282 | implementation uses the :class:`.Signer` base class.
283 | """
284 | if salt is None:
285 | salt = self.salt
286 |
287 | return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs)
288 |
289 | def iter_unsigners(self, salt: str | bytes | None = None) -> cabc.Iterator[Signer]:
290 | """Iterates over all signers to be tried for unsigning. Starts
291 | with the configured signer, then constructs each signer
292 | specified in ``fallback_signers``.
293 | """
294 | if salt is None:
295 | salt = self.salt
296 |
297 | yield self.make_signer(salt)
298 |
299 | for fallback in self.fallback_signers:
300 | if isinstance(fallback, dict):
301 | kwargs = fallback
302 | fallback = self.signer
303 | elif isinstance(fallback, tuple):
304 | fallback, kwargs = fallback
305 | else:
306 | kwargs = self.signer_kwargs
307 |
308 | for secret_key in self.secret_keys:
309 | yield fallback(secret_key, salt=salt, **kwargs)
310 |
311 | def dumps(self, obj: t.Any, salt: str | bytes | None = None) -> _TSerialized:
312 | """Returns a signed string serialized with the internal
313 | serializer. The return value can be either a byte or unicode
314 | string depending on the format of the internal serializer.
315 | """
316 | payload = want_bytes(self.dump_payload(obj))
317 | rv = self.make_signer(salt).sign(payload)
318 |
319 | if self.is_text_serializer:
320 | return rv.decode("utf-8") # type: ignore[return-value]
321 |
322 | return rv # type: ignore[return-value]
323 |
324 | def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> None:
325 | """Like :meth:`dumps` but dumps into a file. The file handle has
326 | to be compatible with what the internal serializer expects.
327 | """
328 | f.write(self.dumps(obj, salt))
329 |
330 | def loads(
331 | self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any
332 | ) -> t.Any:
333 | """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the
334 | signature validation fails.
335 | """
336 | s = want_bytes(s)
337 | last_exception = None
338 |
339 | for signer in self.iter_unsigners(salt):
340 | try:
341 | return self.load_payload(signer.unsign(s))
342 | except BadSignature as err:
343 | last_exception = err
344 |
345 | raise t.cast(BadSignature, last_exception)
346 |
347 | def load(self, f: t.IO[t.Any], salt: str | bytes | None = None) -> t.Any:
348 | """Like :meth:`loads` but loads from a file."""
349 | return self.loads(f.read(), salt)
350 |
351 | def loads_unsafe(
352 | self, s: str | bytes, salt: str | bytes | None = None
353 | ) -> tuple[bool, t.Any]:
354 | """Like :meth:`loads` but without verifying the signature. This
355 | is potentially very dangerous to use depending on how your
356 | serializer works. The return value is ``(signature_valid,
357 | payload)`` instead of just the payload. The first item will be a
358 | boolean that indicates if the signature is valid. This function
359 | never fails.
360 |
361 | Use it for debugging only and if you know that your serializer
362 | module is not exploitable (for example, do not use it with a
363 | pickle serializer).
364 |
365 | .. versionadded:: 0.15
366 | """
367 | return self._loads_unsafe_impl(s, salt)
368 |
369 | def _loads_unsafe_impl(
370 | self,
371 | s: str | bytes,
372 | salt: str | bytes | None,
373 | load_kwargs: dict[str, t.Any] | None = None,
374 | load_payload_kwargs: dict[str, t.Any] | None = None,
375 | ) -> tuple[bool, t.Any]:
376 | """Low level helper function to implement :meth:`loads_unsafe`
377 | in serializer subclasses.
378 | """
379 | if load_kwargs is None:
380 | load_kwargs = {}
381 |
382 | try:
383 | return True, self.loads(s, salt=salt, **load_kwargs)
384 | except BadSignature as e:
385 | if e.payload is None:
386 | return False, None
387 |
388 | if load_payload_kwargs is None:
389 | load_payload_kwargs = {}
390 |
391 | try:
392 | return (
393 | False,
394 | self.load_payload(e.payload, **load_payload_kwargs),
395 | )
396 | except BadPayload:
397 | return False, None
398 |
399 | def load_unsafe(
400 | self, f: t.IO[t.Any], salt: str | bytes | None = None
401 | ) -> tuple[bool, t.Any]:
402 | """Like :meth:`loads_unsafe` but loads from a file.
403 |
404 | .. versionadded:: 0.15
405 | """
406 | return self.loads_unsafe(f.read(), salt=salt)
407 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/click/testing.py:
--------------------------------------------------------------------------------
```python
1 | import contextlib
2 | import io
3 | import os
4 | import shlex
5 | import shutil
6 | import sys
7 | import tempfile
8 | import typing as t
9 | from types import TracebackType
10 |
11 | from . import formatting
12 | from . import termui
13 | from . import utils
14 | from ._compat import _find_binary_reader
15 |
16 | if t.TYPE_CHECKING:
17 | from .core import BaseCommand
18 |
19 |
20 | class EchoingStdin:
21 | def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None:
22 | self._input = input
23 | self._output = output
24 | self._paused = False
25 |
26 | def __getattr__(self, x: str) -> t.Any:
27 | return getattr(self._input, x)
28 |
29 | def _echo(self, rv: bytes) -> bytes:
30 | if not self._paused:
31 | self._output.write(rv)
32 |
33 | return rv
34 |
35 | def read(self, n: int = -1) -> bytes:
36 | return self._echo(self._input.read(n))
37 |
38 | def read1(self, n: int = -1) -> bytes:
39 | return self._echo(self._input.read1(n)) # type: ignore
40 |
41 | def readline(self, n: int = -1) -> bytes:
42 | return self._echo(self._input.readline(n))
43 |
44 | def readlines(self) -> t.List[bytes]:
45 | return [self._echo(x) for x in self._input.readlines()]
46 |
47 | def __iter__(self) -> t.Iterator[bytes]:
48 | return iter(self._echo(x) for x in self._input)
49 |
50 | def __repr__(self) -> str:
51 | return repr(self._input)
52 |
53 |
54 | @contextlib.contextmanager
55 | def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]:
56 | if stream is None:
57 | yield
58 | else:
59 | stream._paused = True
60 | yield
61 | stream._paused = False
62 |
63 |
64 | class _NamedTextIOWrapper(io.TextIOWrapper):
65 | def __init__(
66 | self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any
67 | ) -> None:
68 | super().__init__(buffer, **kwargs)
69 | self._name = name
70 | self._mode = mode
71 |
72 | @property
73 | def name(self) -> str:
74 | return self._name
75 |
76 | @property
77 | def mode(self) -> str:
78 | return self._mode
79 |
80 |
81 | def make_input_stream(
82 | input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str
83 | ) -> t.BinaryIO:
84 | # Is already an input stream.
85 | if hasattr(input, "read"):
86 | rv = _find_binary_reader(t.cast(t.IO[t.Any], input))
87 |
88 | if rv is not None:
89 | return rv
90 |
91 | raise TypeError("Could not find binary reader for input stream.")
92 |
93 | if input is None:
94 | input = b""
95 | elif isinstance(input, str):
96 | input = input.encode(charset)
97 |
98 | return io.BytesIO(input)
99 |
100 |
101 | class Result:
102 | """Holds the captured result of an invoked CLI script."""
103 |
104 | def __init__(
105 | self,
106 | runner: "CliRunner",
107 | stdout_bytes: bytes,
108 | stderr_bytes: t.Optional[bytes],
109 | return_value: t.Any,
110 | exit_code: int,
111 | exception: t.Optional[BaseException],
112 | exc_info: t.Optional[
113 | t.Tuple[t.Type[BaseException], BaseException, TracebackType]
114 | ] = None,
115 | ):
116 | #: The runner that created the result
117 | self.runner = runner
118 | #: The standard output as bytes.
119 | self.stdout_bytes = stdout_bytes
120 | #: The standard error as bytes, or None if not available
121 | self.stderr_bytes = stderr_bytes
122 | #: The value returned from the invoked command.
123 | #:
124 | #: .. versionadded:: 8.0
125 | self.return_value = return_value
126 | #: The exit code as integer.
127 | self.exit_code = exit_code
128 | #: The exception that happened if one did.
129 | self.exception = exception
130 | #: The traceback
131 | self.exc_info = exc_info
132 |
133 | @property
134 | def output(self) -> str:
135 | """The (standard) output as unicode string."""
136 | return self.stdout
137 |
138 | @property
139 | def stdout(self) -> str:
140 | """The standard output as unicode string."""
141 | return self.stdout_bytes.decode(self.runner.charset, "replace").replace(
142 | "\r\n", "\n"
143 | )
144 |
145 | @property
146 | def stderr(self) -> str:
147 | """The standard error as unicode string."""
148 | if self.stderr_bytes is None:
149 | raise ValueError("stderr not separately captured")
150 | return self.stderr_bytes.decode(self.runner.charset, "replace").replace(
151 | "\r\n", "\n"
152 | )
153 |
154 | def __repr__(self) -> str:
155 | exc_str = repr(self.exception) if self.exception else "okay"
156 | return f"<{type(self).__name__} {exc_str}>"
157 |
158 |
159 | class CliRunner:
160 | """The CLI runner provides functionality to invoke a Click command line
161 | script for unittesting purposes in a isolated environment. This only
162 | works in single-threaded systems without any concurrency as it changes the
163 | global interpreter state.
164 |
165 | :param charset: the character set for the input and output data.
166 | :param env: a dictionary with environment variables for overriding.
167 | :param echo_stdin: if this is set to `True`, then reading from stdin writes
168 | to stdout. This is useful for showing examples in
169 | some circumstances. Note that regular prompts
170 | will automatically echo the input.
171 | :param mix_stderr: if this is set to `False`, then stdout and stderr are
172 | preserved as independent streams. This is useful for
173 | Unix-philosophy apps that have predictable stdout and
174 | noisy stderr, such that each may be measured
175 | independently
176 | """
177 |
178 | def __init__(
179 | self,
180 | charset: str = "utf-8",
181 | env: t.Optional[t.Mapping[str, t.Optional[str]]] = None,
182 | echo_stdin: bool = False,
183 | mix_stderr: bool = True,
184 | ) -> None:
185 | self.charset = charset
186 | self.env: t.Mapping[str, t.Optional[str]] = env or {}
187 | self.echo_stdin = echo_stdin
188 | self.mix_stderr = mix_stderr
189 |
190 | def get_default_prog_name(self, cli: "BaseCommand") -> str:
191 | """Given a command object it will return the default program name
192 | for it. The default is the `name` attribute or ``"root"`` if not
193 | set.
194 | """
195 | return cli.name or "root"
196 |
197 | def make_env(
198 | self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None
199 | ) -> t.Mapping[str, t.Optional[str]]:
200 | """Returns the environment overrides for invoking a script."""
201 | rv = dict(self.env)
202 | if overrides:
203 | rv.update(overrides)
204 | return rv
205 |
206 | @contextlib.contextmanager
207 | def isolation(
208 | self,
209 | input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None,
210 | env: t.Optional[t.Mapping[str, t.Optional[str]]] = None,
211 | color: bool = False,
212 | ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]:
213 | """A context manager that sets up the isolation for invoking of a
214 | command line tool. This sets up stdin with the given input data
215 | and `os.environ` with the overrides from the given dictionary.
216 | This also rebinds some internals in Click to be mocked (like the
217 | prompt functionality).
218 |
219 | This is automatically done in the :meth:`invoke` method.
220 |
221 | :param input: the input stream to put into sys.stdin.
222 | :param env: the environment overrides as dictionary.
223 | :param color: whether the output should contain color codes. The
224 | application can still override this explicitly.
225 |
226 | .. versionchanged:: 8.0
227 | ``stderr`` is opened with ``errors="backslashreplace"``
228 | instead of the default ``"strict"``.
229 |
230 | .. versionchanged:: 4.0
231 | Added the ``color`` parameter.
232 | """
233 | bytes_input = make_input_stream(input, self.charset)
234 | echo_input = None
235 |
236 | old_stdin = sys.stdin
237 | old_stdout = sys.stdout
238 | old_stderr = sys.stderr
239 | old_forced_width = formatting.FORCED_WIDTH
240 | formatting.FORCED_WIDTH = 80
241 |
242 | env = self.make_env(env)
243 |
244 | bytes_output = io.BytesIO()
245 |
246 | if self.echo_stdin:
247 | bytes_input = echo_input = t.cast(
248 | t.BinaryIO, EchoingStdin(bytes_input, bytes_output)
249 | )
250 |
251 | sys.stdin = text_input = _NamedTextIOWrapper(
252 | bytes_input, encoding=self.charset, name="<stdin>", mode="r"
253 | )
254 |
255 | if self.echo_stdin:
256 | # Force unbuffered reads, otherwise TextIOWrapper reads a
257 | # large chunk which is echoed early.
258 | text_input._CHUNK_SIZE = 1 # type: ignore
259 |
260 | sys.stdout = _NamedTextIOWrapper(
261 | bytes_output, encoding=self.charset, name="<stdout>", mode="w"
262 | )
263 |
264 | bytes_error = None
265 | if self.mix_stderr:
266 | sys.stderr = sys.stdout
267 | else:
268 | bytes_error = io.BytesIO()
269 | sys.stderr = _NamedTextIOWrapper(
270 | bytes_error,
271 | encoding=self.charset,
272 | name="<stderr>",
273 | mode="w",
274 | errors="backslashreplace",
275 | )
276 |
277 | @_pause_echo(echo_input) # type: ignore
278 | def visible_input(prompt: t.Optional[str] = None) -> str:
279 | sys.stdout.write(prompt or "")
280 | val = text_input.readline().rstrip("\r\n")
281 | sys.stdout.write(f"{val}\n")
282 | sys.stdout.flush()
283 | return val
284 |
285 | @_pause_echo(echo_input) # type: ignore
286 | def hidden_input(prompt: t.Optional[str] = None) -> str:
287 | sys.stdout.write(f"{prompt or ''}\n")
288 | sys.stdout.flush()
289 | return text_input.readline().rstrip("\r\n")
290 |
291 | @_pause_echo(echo_input) # type: ignore
292 | def _getchar(echo: bool) -> str:
293 | char = sys.stdin.read(1)
294 |
295 | if echo:
296 | sys.stdout.write(char)
297 |
298 | sys.stdout.flush()
299 | return char
300 |
301 | default_color = color
302 |
303 | def should_strip_ansi(
304 | stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None
305 | ) -> bool:
306 | if color is None:
307 | return not default_color
308 | return not color
309 |
310 | old_visible_prompt_func = termui.visible_prompt_func
311 | old_hidden_prompt_func = termui.hidden_prompt_func
312 | old__getchar_func = termui._getchar
313 | old_should_strip_ansi = utils.should_strip_ansi # type: ignore
314 | termui.visible_prompt_func = visible_input
315 | termui.hidden_prompt_func = hidden_input
316 | termui._getchar = _getchar
317 | utils.should_strip_ansi = should_strip_ansi # type: ignore
318 |
319 | old_env = {}
320 | try:
321 | for key, value in env.items():
322 | old_env[key] = os.environ.get(key)
323 | if value is None:
324 | try:
325 | del os.environ[key]
326 | except Exception:
327 | pass
328 | else:
329 | os.environ[key] = value
330 | yield (bytes_output, bytes_error)
331 | finally:
332 | for key, value in old_env.items():
333 | if value is None:
334 | try:
335 | del os.environ[key]
336 | except Exception:
337 | pass
338 | else:
339 | os.environ[key] = value
340 | sys.stdout = old_stdout
341 | sys.stderr = old_stderr
342 | sys.stdin = old_stdin
343 | termui.visible_prompt_func = old_visible_prompt_func
344 | termui.hidden_prompt_func = old_hidden_prompt_func
345 | termui._getchar = old__getchar_func
346 | utils.should_strip_ansi = old_should_strip_ansi # type: ignore
347 | formatting.FORCED_WIDTH = old_forced_width
348 |
349 | def invoke(
350 | self,
351 | cli: "BaseCommand",
352 | args: t.Optional[t.Union[str, t.Sequence[str]]] = None,
353 | input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None,
354 | env: t.Optional[t.Mapping[str, t.Optional[str]]] = None,
355 | catch_exceptions: bool = True,
356 | color: bool = False,
357 | **extra: t.Any,
358 | ) -> Result:
359 | """Invokes a command in an isolated environment. The arguments are
360 | forwarded directly to the command line script, the `extra` keyword
361 | arguments are passed to the :meth:`~clickpkg.Command.main` function of
362 | the command.
363 |
364 | This returns a :class:`Result` object.
365 |
366 | :param cli: the command to invoke
367 | :param args: the arguments to invoke. It may be given as an iterable
368 | or a string. When given as string it will be interpreted
369 | as a Unix shell command. More details at
370 | :func:`shlex.split`.
371 | :param input: the input data for `sys.stdin`.
372 | :param env: the environment overrides.
373 | :param catch_exceptions: Whether to catch any other exceptions than
374 | ``SystemExit``.
375 | :param extra: the keyword arguments to pass to :meth:`main`.
376 | :param color: whether the output should contain color codes. The
377 | application can still override this explicitly.
378 |
379 | .. versionchanged:: 8.0
380 | The result object has the ``return_value`` attribute with
381 | the value returned from the invoked command.
382 |
383 | .. versionchanged:: 4.0
384 | Added the ``color`` parameter.
385 |
386 | .. versionchanged:: 3.0
387 | Added the ``catch_exceptions`` parameter.
388 |
389 | .. versionchanged:: 3.0
390 | The result object has the ``exc_info`` attribute with the
391 | traceback if available.
392 | """
393 | exc_info = None
394 | with self.isolation(input=input, env=env, color=color) as outstreams:
395 | return_value = None
396 | exception: t.Optional[BaseException] = None
397 | exit_code = 0
398 |
399 | if isinstance(args, str):
400 | args = shlex.split(args)
401 |
402 | try:
403 | prog_name = extra.pop("prog_name")
404 | except KeyError:
405 | prog_name = self.get_default_prog_name(cli)
406 |
407 | try:
408 | return_value = cli.main(args=args or (), prog_name=prog_name, **extra)
409 | except SystemExit as e:
410 | exc_info = sys.exc_info()
411 | e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code)
412 |
413 | if e_code is None:
414 | e_code = 0
415 |
416 | if e_code != 0:
417 | exception = e
418 |
419 | if not isinstance(e_code, int):
420 | sys.stdout.write(str(e_code))
421 | sys.stdout.write("\n")
422 | e_code = 1
423 |
424 | exit_code = e_code
425 |
426 | except Exception as e:
427 | if not catch_exceptions:
428 | raise
429 | exception = e
430 | exit_code = 1
431 | exc_info = sys.exc_info()
432 | finally:
433 | sys.stdout.flush()
434 | stdout = outstreams[0].getvalue()
435 | if self.mix_stderr:
436 | stderr = None
437 | else:
438 | stderr = outstreams[1].getvalue() # type: ignore
439 |
440 | return Result(
441 | runner=self,
442 | stdout_bytes=stdout,
443 | stderr_bytes=stderr,
444 | return_value=return_value,
445 | exit_code=exit_code,
446 | exception=exception,
447 | exc_info=exc_info, # type: ignore
448 | )
449 |
450 | @contextlib.contextmanager
451 | def isolated_filesystem(
452 | self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None
453 | ) -> t.Iterator[str]:
454 | """A context manager that creates a temporary directory and
455 | changes the current working directory to it. This isolates tests
456 | that affect the contents of the CWD to prevent them from
457 | interfering with each other.
458 |
459 | :param temp_dir: Create the temporary directory under this
460 | directory. If given, the created directory is not removed
461 | when exiting.
462 |
463 | .. versionchanged:: 8.0
464 | Added the ``temp_dir`` parameter.
465 | """
466 | cwd = os.getcwd()
467 | dt = tempfile.mkdtemp(dir=temp_dir)
468 | os.chdir(dt)
469 |
470 | try:
471 | yield dt
472 | finally:
473 | os.chdir(cwd)
474 |
475 | if temp_dir is None:
476 | try:
477 | shutil.rmtree(dt)
478 | except OSError: # noqa: B014
479 | pass
480 |
```