This is page 146 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/test.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import dataclasses
4 | import mimetypes
5 | import sys
6 | import typing as t
7 | from collections import defaultdict
8 | from datetime import datetime
9 | from io import BytesIO
10 | from itertools import chain
11 | from random import random
12 | from tempfile import TemporaryFile
13 | from time import time
14 | from urllib.parse import unquote
15 | from urllib.parse import urlsplit
16 | from urllib.parse import urlunsplit
17 |
18 | from ._internal import _get_environ
19 | from ._internal import _wsgi_decoding_dance
20 | from ._internal import _wsgi_encoding_dance
21 | from .datastructures import Authorization
22 | from .datastructures import CallbackDict
23 | from .datastructures import CombinedMultiDict
24 | from .datastructures import EnvironHeaders
25 | from .datastructures import FileMultiDict
26 | from .datastructures import Headers
27 | from .datastructures import MultiDict
28 | from .http import dump_cookie
29 | from .http import dump_options_header
30 | from .http import parse_cookie
31 | from .http import parse_date
32 | from .http import parse_options_header
33 | from .sansio.multipart import Data
34 | from .sansio.multipart import Epilogue
35 | from .sansio.multipart import Field
36 | from .sansio.multipart import File
37 | from .sansio.multipart import MultipartEncoder
38 | from .sansio.multipart import Preamble
39 | from .urls import _urlencode
40 | from .urls import iri_to_uri
41 | from .utils import cached_property
42 | from .utils import get_content_type
43 | from .wrappers.request import Request
44 | from .wrappers.response import Response
45 | from .wsgi import ClosingIterator
46 | from .wsgi import get_current_url
47 |
48 | if t.TYPE_CHECKING:
49 | import typing_extensions as te
50 | from _typeshed.wsgi import WSGIApplication
51 | from _typeshed.wsgi import WSGIEnvironment
52 |
53 |
54 | def stream_encode_multipart(
55 | data: t.Mapping[str, t.Any],
56 | use_tempfile: bool = True,
57 | threshold: int = 1024 * 500,
58 | boundary: str | None = None,
59 | ) -> tuple[t.IO[bytes], int, str]:
60 | """Encode a dict of values (either strings or file descriptors or
61 | :class:`FileStorage` objects.) into a multipart encoded string stored
62 | in a file descriptor.
63 |
64 | .. versionchanged:: 3.0
65 | The ``charset`` parameter was removed.
66 | """
67 | if boundary is None:
68 | boundary = f"---------------WerkzeugFormPart_{time()}{random()}"
69 |
70 | stream: t.IO[bytes] = BytesIO()
71 | total_length = 0
72 | on_disk = False
73 | write_binary: t.Callable[[bytes], int]
74 |
75 | if use_tempfile:
76 |
77 | def write_binary(s: bytes) -> int:
78 | nonlocal stream, total_length, on_disk
79 |
80 | if on_disk:
81 | return stream.write(s)
82 | else:
83 | length = len(s)
84 |
85 | if length + total_length <= threshold:
86 | stream.write(s)
87 | else:
88 | new_stream = t.cast(t.IO[bytes], TemporaryFile("wb+"))
89 | new_stream.write(stream.getvalue()) # type: ignore
90 | new_stream.write(s)
91 | stream = new_stream
92 | on_disk = True
93 |
94 | total_length += length
95 | return length
96 |
97 | else:
98 | write_binary = stream.write
99 |
100 | encoder = MultipartEncoder(boundary.encode())
101 | write_binary(encoder.send_event(Preamble(data=b"")))
102 | for key, value in _iter_data(data):
103 | reader = getattr(value, "read", None)
104 | if reader is not None:
105 | filename = getattr(value, "filename", getattr(value, "name", None))
106 | content_type = getattr(value, "content_type", None)
107 | if content_type is None:
108 | content_type = (
109 | filename
110 | and mimetypes.guess_type(filename)[0]
111 | or "application/octet-stream"
112 | )
113 | headers = value.headers
114 | headers.update([("Content-Type", content_type)])
115 | if filename is None:
116 | write_binary(encoder.send_event(Field(name=key, headers=headers)))
117 | else:
118 | write_binary(
119 | encoder.send_event(
120 | File(name=key, filename=filename, headers=headers)
121 | )
122 | )
123 | while True:
124 | chunk = reader(16384)
125 |
126 | if not chunk:
127 | write_binary(encoder.send_event(Data(data=chunk, more_data=False)))
128 | break
129 |
130 | write_binary(encoder.send_event(Data(data=chunk, more_data=True)))
131 | else:
132 | if not isinstance(value, str):
133 | value = str(value)
134 | write_binary(encoder.send_event(Field(name=key, headers=Headers())))
135 | write_binary(encoder.send_event(Data(data=value.encode(), more_data=False)))
136 |
137 | write_binary(encoder.send_event(Epilogue(data=b"")))
138 |
139 | length = stream.tell()
140 | stream.seek(0)
141 | return stream, length, boundary
142 |
143 |
144 | def encode_multipart(
145 | values: t.Mapping[str, t.Any], boundary: str | None = None
146 | ) -> tuple[str, bytes]:
147 | """Like `stream_encode_multipart` but returns a tuple in the form
148 | (``boundary``, ``data``) where data is bytes.
149 |
150 | .. versionchanged:: 3.0
151 | The ``charset`` parameter was removed.
152 | """
153 | stream, length, boundary = stream_encode_multipart(
154 | values, use_tempfile=False, boundary=boundary
155 | )
156 | return boundary, stream.read()
157 |
158 |
159 | def _iter_data(data: t.Mapping[str, t.Any]) -> t.Iterator[tuple[str, t.Any]]:
160 | """Iterate over a mapping that might have a list of values, yielding
161 | all key, value pairs. Almost like iter_multi_items but only allows
162 | lists, not tuples, of values so tuples can be used for files.
163 | """
164 | if isinstance(data, MultiDict):
165 | yield from data.items(multi=True)
166 | else:
167 | for key, value in data.items():
168 | if isinstance(value, list):
169 | for v in value:
170 | yield key, v
171 | else:
172 | yield key, value
173 |
174 |
175 | _TAnyMultiDict = t.TypeVar("_TAnyMultiDict", bound="MultiDict[t.Any, t.Any]")
176 |
177 |
178 | class EnvironBuilder:
179 | """This class can be used to conveniently create a WSGI environment
180 | for testing purposes. It can be used to quickly create WSGI environments
181 | or request objects from arbitrary data.
182 |
183 | The signature of this class is also used in some other places as of
184 | Werkzeug 0.5 (:func:`create_environ`, :meth:`Response.from_values`,
185 | :meth:`Client.open`). Because of this most of the functionality is
186 | available through the constructor alone.
187 |
188 | Files and regular form data can be manipulated independently of each
189 | other with the :attr:`form` and :attr:`files` attributes, but are
190 | passed with the same argument to the constructor: `data`.
191 |
192 | `data` can be any of these values:
193 |
194 | - a `str` or `bytes` object: The object is converted into an
195 | :attr:`input_stream`, the :attr:`content_length` is set and you have to
196 | provide a :attr:`content_type`.
197 | - a `dict` or :class:`MultiDict`: The keys have to be strings. The values
198 | have to be either any of the following objects, or a list of any of the
199 | following objects:
200 |
201 | - a :class:`file`-like object: These are converted into
202 | :class:`FileStorage` objects automatically.
203 | - a `tuple`: The :meth:`~FileMultiDict.add_file` method is called
204 | with the key and the unpacked `tuple` items as positional
205 | arguments.
206 | - a `str`: The string is set as form data for the associated key.
207 | - a file-like object: The object content is loaded in memory and then
208 | handled like a regular `str` or a `bytes`.
209 |
210 | :param path: the path of the request. In the WSGI environment this will
211 | end up as `PATH_INFO`. If the `query_string` is not defined
212 | and there is a question mark in the `path` everything after
213 | it is used as query string.
214 | :param base_url: the base URL is a URL that is used to extract the WSGI
215 | URL scheme, host (server name + server port) and the
216 | script root (`SCRIPT_NAME`).
217 | :param query_string: an optional string or dict with URL parameters.
218 | :param method: the HTTP method to use, defaults to `GET`.
219 | :param input_stream: an optional input stream. Do not specify this and
220 | `data`. As soon as an input stream is set you can't
221 | modify :attr:`args` and :attr:`files` unless you
222 | set the :attr:`input_stream` to `None` again.
223 | :param content_type: The content type for the request. As of 0.5 you
224 | don't have to provide this when specifying files
225 | and form data via `data`.
226 | :param content_length: The content length for the request. You don't
227 | have to specify this when providing data via
228 | `data`.
229 | :param errors_stream: an optional error stream that is used for
230 | `wsgi.errors`. Defaults to :data:`stderr`.
231 | :param multithread: controls `wsgi.multithread`. Defaults to `False`.
232 | :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`.
233 | :param run_once: controls `wsgi.run_once`. Defaults to `False`.
234 | :param headers: an optional list or :class:`Headers` object of headers.
235 | :param data: a string or dict of form data or a file-object.
236 | See explanation above.
237 | :param json: An object to be serialized and assigned to ``data``.
238 | Defaults the content type to ``"application/json"``.
239 | Serialized with the function assigned to :attr:`json_dumps`.
240 | :param environ_base: an optional dict of environment defaults.
241 | :param environ_overrides: an optional dict of environment overrides.
242 | :param auth: An authorization object to use for the
243 | ``Authorization`` header value. A ``(username, password)`` tuple
244 | is a shortcut for ``Basic`` authorization.
245 |
246 | .. versionchanged:: 3.0
247 | The ``charset`` parameter was removed.
248 |
249 | .. versionchanged:: 2.1
250 | ``CONTENT_TYPE`` and ``CONTENT_LENGTH`` are not duplicated as
251 | header keys in the environ.
252 |
253 | .. versionchanged:: 2.0
254 | ``REQUEST_URI`` and ``RAW_URI`` is the full raw URI including
255 | the query string, not only the path.
256 |
257 | .. versionchanged:: 2.0
258 | The default :attr:`request_class` is ``Request`` instead of
259 | ``BaseRequest``.
260 |
261 | .. versionadded:: 2.0
262 | Added the ``auth`` parameter.
263 |
264 | .. versionadded:: 0.15
265 | The ``json`` param and :meth:`json_dumps` method.
266 |
267 | .. versionadded:: 0.15
268 | The environ has keys ``REQUEST_URI`` and ``RAW_URI`` containing
269 | the path before percent-decoding. This is not part of the WSGI
270 | PEP, but many WSGI servers include it.
271 |
272 | .. versionchanged:: 0.6
273 | ``path`` and ``base_url`` can now be unicode strings that are
274 | encoded with :func:`iri_to_uri`.
275 | """
276 |
277 | #: the server protocol to use. defaults to HTTP/1.1
278 | server_protocol = "HTTP/1.1"
279 |
280 | #: the wsgi version to use. defaults to (1, 0)
281 | wsgi_version = (1, 0)
282 |
283 | #: The default request class used by :meth:`get_request`.
284 | request_class = Request
285 |
286 | import json
287 |
288 | #: The serialization function used when ``json`` is passed.
289 | json_dumps = staticmethod(json.dumps)
290 | del json
291 |
292 | _args: MultiDict[str, str] | None
293 | _query_string: str | None
294 | _input_stream: t.IO[bytes] | None
295 | _form: MultiDict[str, str] | None
296 | _files: FileMultiDict | None
297 |
298 | def __init__(
299 | self,
300 | path: str = "/",
301 | base_url: str | None = None,
302 | query_string: t.Mapping[str, str] | str | None = None,
303 | method: str = "GET",
304 | input_stream: t.IO[bytes] | None = None,
305 | content_type: str | None = None,
306 | content_length: int | None = None,
307 | errors_stream: t.IO[str] | None = None,
308 | multithread: bool = False,
309 | multiprocess: bool = False,
310 | run_once: bool = False,
311 | headers: Headers | t.Iterable[tuple[str, str]] | None = None,
312 | data: None | (t.IO[bytes] | str | bytes | t.Mapping[str, t.Any]) = None,
313 | environ_base: t.Mapping[str, t.Any] | None = None,
314 | environ_overrides: t.Mapping[str, t.Any] | None = None,
315 | mimetype: str | None = None,
316 | json: t.Mapping[str, t.Any] | None = None,
317 | auth: Authorization | tuple[str, str] | None = None,
318 | ) -> None:
319 | if query_string is not None and "?" in path:
320 | raise ValueError("Query string is defined in the path and as an argument")
321 | request_uri = urlsplit(path)
322 | if query_string is None and "?" in path:
323 | query_string = request_uri.query
324 |
325 | self.path = iri_to_uri(request_uri.path)
326 | self.request_uri = path
327 | if base_url is not None:
328 | base_url = iri_to_uri(base_url)
329 | self.base_url = base_url # type: ignore
330 | if isinstance(query_string, str):
331 | self.query_string = query_string
332 | else:
333 | if query_string is None:
334 | query_string = MultiDict()
335 | elif not isinstance(query_string, MultiDict):
336 | query_string = MultiDict(query_string)
337 | self.args = query_string
338 | self.method = method
339 | if headers is None:
340 | headers = Headers()
341 | elif not isinstance(headers, Headers):
342 | headers = Headers(headers)
343 | self.headers = headers
344 | if content_type is not None:
345 | self.content_type = content_type
346 | if errors_stream is None:
347 | errors_stream = sys.stderr
348 | self.errors_stream = errors_stream
349 | self.multithread = multithread
350 | self.multiprocess = multiprocess
351 | self.run_once = run_once
352 | self.environ_base = environ_base
353 | self.environ_overrides = environ_overrides
354 | self.input_stream = input_stream
355 | self.content_length = content_length
356 | self.closed = False
357 |
358 | if auth is not None:
359 | if isinstance(auth, tuple):
360 | auth = Authorization(
361 | "basic", {"username": auth[0], "password": auth[1]}
362 | )
363 |
364 | self.headers.set("Authorization", auth.to_header())
365 |
366 | if json is not None:
367 | if data is not None:
368 | raise TypeError("can't provide both json and data")
369 |
370 | data = self.json_dumps(json)
371 |
372 | if self.content_type is None:
373 | self.content_type = "application/json"
374 |
375 | if data:
376 | if input_stream is not None:
377 | raise TypeError("can't provide input stream and data")
378 | if hasattr(data, "read"):
379 | data = data.read()
380 | if isinstance(data, str):
381 | data = data.encode()
382 | if isinstance(data, bytes):
383 | self.input_stream = BytesIO(data)
384 | if self.content_length is None:
385 | self.content_length = len(data)
386 | else:
387 | for key, value in _iter_data(data):
388 | if isinstance(value, (tuple, dict)) or hasattr(value, "read"):
389 | self._add_file_from_data(key, value)
390 | else:
391 | self.form.setlistdefault(key).append(value)
392 |
393 | if mimetype is not None:
394 | self.mimetype = mimetype
395 |
396 | @classmethod
397 | def from_environ(cls, environ: WSGIEnvironment, **kwargs: t.Any) -> EnvironBuilder:
398 | """Turn an environ dict back into a builder. Any extra kwargs
399 | override the args extracted from the environ.
400 |
401 | .. versionchanged:: 2.0
402 | Path and query values are passed through the WSGI decoding
403 | dance to avoid double encoding.
404 |
405 | .. versionadded:: 0.15
406 | """
407 | headers = Headers(EnvironHeaders(environ))
408 | out = {
409 | "path": _wsgi_decoding_dance(environ["PATH_INFO"]),
410 | "base_url": cls._make_base_url(
411 | environ["wsgi.url_scheme"],
412 | headers.pop("Host"),
413 | _wsgi_decoding_dance(environ["SCRIPT_NAME"]),
414 | ),
415 | "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]),
416 | "method": environ["REQUEST_METHOD"],
417 | "input_stream": environ["wsgi.input"],
418 | "content_type": headers.pop("Content-Type", None),
419 | "content_length": headers.pop("Content-Length", None),
420 | "errors_stream": environ["wsgi.errors"],
421 | "multithread": environ["wsgi.multithread"],
422 | "multiprocess": environ["wsgi.multiprocess"],
423 | "run_once": environ["wsgi.run_once"],
424 | "headers": headers,
425 | }
426 | out.update(kwargs)
427 | return cls(**out)
428 |
429 | def _add_file_from_data(
430 | self,
431 | key: str,
432 | value: (t.IO[bytes] | tuple[t.IO[bytes], str] | tuple[t.IO[bytes], str, str]),
433 | ) -> None:
434 | """Called in the EnvironBuilder to add files from the data dict."""
435 | if isinstance(value, tuple):
436 | self.files.add_file(key, *value)
437 | else:
438 | self.files.add_file(key, value)
439 |
440 | @staticmethod
441 | def _make_base_url(scheme: str, host: str, script_root: str) -> str:
442 | return urlunsplit((scheme, host, script_root, "", "")).rstrip("/") + "/"
443 |
444 | @property
445 | def base_url(self) -> str:
446 | """The base URL is used to extract the URL scheme, host name,
447 | port, and root path.
448 | """
449 | return self._make_base_url(self.url_scheme, self.host, self.script_root)
450 |
451 | @base_url.setter
452 | def base_url(self, value: str | None) -> None:
453 | if value is None:
454 | scheme = "http"
455 | netloc = "localhost"
456 | script_root = ""
457 | else:
458 | scheme, netloc, script_root, qs, anchor = urlsplit(value)
459 | if qs or anchor:
460 | raise ValueError("base url must not contain a query string or fragment")
461 | self.script_root = script_root.rstrip("/")
462 | self.host = netloc
463 | self.url_scheme = scheme
464 |
465 | @property
466 | def content_type(self) -> str | None:
467 | """The content type for the request. Reflected from and to
468 | the :attr:`headers`. Do not set if you set :attr:`files` or
469 | :attr:`form` for auto detection.
470 | """
471 | ct = self.headers.get("Content-Type")
472 | if ct is None and not self._input_stream:
473 | if self._files:
474 | return "multipart/form-data"
475 | if self._form:
476 | return "application/x-www-form-urlencoded"
477 | return None
478 | return ct
479 |
480 | @content_type.setter
481 | def content_type(self, value: str | None) -> None:
482 | if value is None:
483 | self.headers.pop("Content-Type", None)
484 | else:
485 | self.headers["Content-Type"] = value
486 |
487 | @property
488 | def mimetype(self) -> str | None:
489 | """The mimetype (content type without charset etc.)
490 |
491 | .. versionadded:: 0.14
492 | """
493 | ct = self.content_type
494 | return ct.split(";")[0].strip() if ct else None
495 |
496 | @mimetype.setter
497 | def mimetype(self, value: str) -> None:
498 | self.content_type = get_content_type(value, "utf-8")
499 |
500 | @property
501 | def mimetype_params(self) -> t.Mapping[str, str]:
502 | """The mimetype parameters as dict. For example if the
503 | content type is ``text/html; charset=utf-8`` the params would be
504 | ``{'charset': 'utf-8'}``.
505 |
506 | .. versionadded:: 0.14
507 | """
508 |
509 | def on_update(d: CallbackDict[str, str]) -> None:
510 | self.headers["Content-Type"] = dump_options_header(self.mimetype, d)
511 |
512 | d = parse_options_header(self.headers.get("content-type", ""))[1]
513 | return CallbackDict(d, on_update)
514 |
515 | @property
516 | def content_length(self) -> int | None:
517 | """The content length as integer. Reflected from and to the
518 | :attr:`headers`. Do not set if you set :attr:`files` or
519 | :attr:`form` for auto detection.
520 | """
521 | return self.headers.get("Content-Length", type=int)
522 |
523 | @content_length.setter
524 | def content_length(self, value: int | None) -> None:
525 | if value is None:
526 | self.headers.pop("Content-Length", None)
527 | else:
528 | self.headers["Content-Length"] = str(value)
529 |
530 | def _get_form(self, name: str, storage: type[_TAnyMultiDict]) -> _TAnyMultiDict:
531 | """Common behavior for getting the :attr:`form` and
532 | :attr:`files` properties.
533 |
534 | :param name: Name of the internal cached attribute.
535 | :param storage: Storage class used for the data.
536 | """
537 | if self.input_stream is not None:
538 | raise AttributeError("an input stream is defined")
539 |
540 | rv = getattr(self, name)
541 |
542 | if rv is None:
543 | rv = storage()
544 | setattr(self, name, rv)
545 |
546 | return rv # type: ignore
547 |
548 | def _set_form(self, name: str, value: MultiDict[str, t.Any]) -> None:
549 | """Common behavior for setting the :attr:`form` and
550 | :attr:`files` properties.
551 |
552 | :param name: Name of the internal cached attribute.
553 | :param value: Value to assign to the attribute.
554 | """
555 | self._input_stream = None
556 | setattr(self, name, value)
557 |
558 | @property
559 | def form(self) -> MultiDict[str, str]:
560 | """A :class:`MultiDict` of form values."""
561 | return self._get_form("_form", MultiDict)
562 |
563 | @form.setter
564 | def form(self, value: MultiDict[str, str]) -> None:
565 | self._set_form("_form", value)
566 |
567 | @property
568 | def files(self) -> FileMultiDict:
569 | """A :class:`FileMultiDict` of uploaded files. Use
570 | :meth:`~FileMultiDict.add_file` to add new files.
571 | """
572 | return self._get_form("_files", FileMultiDict)
573 |
574 | @files.setter
575 | def files(self, value: FileMultiDict) -> None:
576 | self._set_form("_files", value)
577 |
578 | @property
579 | def input_stream(self) -> t.IO[bytes] | None:
580 | """An optional input stream. This is mutually exclusive with
581 | setting :attr:`form` and :attr:`files`, setting it will clear
582 | those. Do not provide this if the method is not ``POST`` or
583 | another method that has a body.
584 | """
585 | return self._input_stream
586 |
587 | @input_stream.setter
588 | def input_stream(self, value: t.IO[bytes] | None) -> None:
589 | self._input_stream = value
590 | self._form = None
591 | self._files = None
592 |
593 | @property
594 | def query_string(self) -> str:
595 | """The query string. If you set this to a string
596 | :attr:`args` will no longer be available.
597 | """
598 | if self._query_string is None:
599 | if self._args is not None:
600 | return _urlencode(self._args)
601 | return ""
602 | return self._query_string
603 |
604 | @query_string.setter
605 | def query_string(self, value: str | None) -> None:
606 | self._query_string = value
607 | self._args = None
608 |
609 | @property
610 | def args(self) -> MultiDict[str, str]:
611 | """The URL arguments as :class:`MultiDict`."""
612 | if self._query_string is not None:
613 | raise AttributeError("a query string is defined")
614 | if self._args is None:
615 | self._args = MultiDict()
616 | return self._args
617 |
618 | @args.setter
619 | def args(self, value: MultiDict[str, str] | None) -> None:
620 | self._query_string = None
621 | self._args = value
622 |
623 | @property
624 | def server_name(self) -> str:
625 | """The server name (read-only, use :attr:`host` to set)"""
626 | return self.host.split(":", 1)[0]
627 |
628 | @property
629 | def server_port(self) -> int:
630 | """The server port as integer (read-only, use :attr:`host` to set)"""
631 | pieces = self.host.split(":", 1)
632 |
633 | if len(pieces) == 2:
634 | try:
635 | return int(pieces[1])
636 | except ValueError:
637 | pass
638 |
639 | if self.url_scheme == "https":
640 | return 443
641 | return 80
642 |
643 | def __del__(self) -> None:
644 | try:
645 | self.close()
646 | except Exception:
647 | pass
648 |
649 | def close(self) -> None:
650 | """Closes all files. If you put real :class:`file` objects into the
651 | :attr:`files` dict you can call this method to automatically close
652 | them all in one go.
653 | """
654 | if self.closed:
655 | return
656 | try:
657 | files = self.files.values()
658 | except AttributeError:
659 | files = () # type: ignore
660 | for f in files:
661 | try:
662 | f.close()
663 | except Exception:
664 | pass
665 | self.closed = True
666 |
667 | def get_environ(self) -> WSGIEnvironment:
668 | """Return the built environ.
669 |
670 | .. versionchanged:: 0.15
671 | The content type and length headers are set based on
672 | input stream detection. Previously this only set the WSGI
673 | keys.
674 | """
675 | input_stream = self.input_stream
676 | content_length = self.content_length
677 |
678 | mimetype = self.mimetype
679 | content_type = self.content_type
680 |
681 | if input_stream is not None:
682 | start_pos = input_stream.tell()
683 | input_stream.seek(0, 2)
684 | end_pos = input_stream.tell()
685 | input_stream.seek(start_pos)
686 | content_length = end_pos - start_pos
687 | elif mimetype == "multipart/form-data":
688 | input_stream, content_length, boundary = stream_encode_multipart(
689 | CombinedMultiDict([self.form, self.files])
690 | )
691 | content_type = f'{mimetype}; boundary="{boundary}"'
692 | elif mimetype == "application/x-www-form-urlencoded":
693 | form_encoded = _urlencode(self.form).encode("ascii")
694 | content_length = len(form_encoded)
695 | input_stream = BytesIO(form_encoded)
696 | else:
697 | input_stream = BytesIO()
698 |
699 | result: WSGIEnvironment = {}
700 | if self.environ_base:
701 | result.update(self.environ_base)
702 |
703 | def _path_encode(x: str) -> str:
704 | return _wsgi_encoding_dance(unquote(x))
705 |
706 | raw_uri = _wsgi_encoding_dance(self.request_uri)
707 | result.update(
708 | {
709 | "REQUEST_METHOD": self.method,
710 | "SCRIPT_NAME": _path_encode(self.script_root),
711 | "PATH_INFO": _path_encode(self.path),
712 | "QUERY_STRING": _wsgi_encoding_dance(self.query_string),
713 | # Non-standard, added by mod_wsgi, uWSGI
714 | "REQUEST_URI": raw_uri,
715 | # Non-standard, added by gunicorn
716 | "RAW_URI": raw_uri,
717 | "SERVER_NAME": self.server_name,
718 | "SERVER_PORT": str(self.server_port),
719 | "HTTP_HOST": self.host,
720 | "SERVER_PROTOCOL": self.server_protocol,
721 | "wsgi.version": self.wsgi_version,
722 | "wsgi.url_scheme": self.url_scheme,
723 | "wsgi.input": input_stream,
724 | "wsgi.errors": self.errors_stream,
725 | "wsgi.multithread": self.multithread,
726 | "wsgi.multiprocess": self.multiprocess,
727 | "wsgi.run_once": self.run_once,
728 | }
729 | )
730 |
731 | headers = self.headers.copy()
732 | # Don't send these as headers, they're part of the environ.
733 | headers.remove("Content-Type")
734 | headers.remove("Content-Length")
735 |
736 | if content_type is not None:
737 | result["CONTENT_TYPE"] = content_type
738 |
739 | if content_length is not None:
740 | result["CONTENT_LENGTH"] = str(content_length)
741 |
742 | combined_headers = defaultdict(list)
743 |
744 | for key, value in headers.to_wsgi_list():
745 | combined_headers[f"HTTP_{key.upper().replace('-', '_')}"].append(value)
746 |
747 | for key, values in combined_headers.items():
748 | result[key] = ", ".join(values)
749 |
750 | if self.environ_overrides:
751 | result.update(self.environ_overrides)
752 |
753 | return result
754 |
755 | def get_request(self, cls: type[Request] | None = None) -> Request:
756 | """Returns a request with the data. If the request class is not
757 | specified :attr:`request_class` is used.
758 |
759 | :param cls: The request wrapper to use.
760 | """
761 | if cls is None:
762 | cls = self.request_class
763 |
764 | return cls(self.get_environ())
765 |
766 |
767 | class ClientRedirectError(Exception):
768 | """If a redirect loop is detected when using follow_redirects=True with
769 | the :cls:`Client`, then this exception is raised.
770 | """
771 |
772 |
773 | class Client:
774 | """Simulate sending requests to a WSGI application without running a WSGI or HTTP
775 | server.
776 |
777 | :param application: The WSGI application to make requests to.
778 | :param response_wrapper: A :class:`.Response` class to wrap response data with.
779 | Defaults to :class:`.TestResponse`. If it's not a subclass of ``TestResponse``,
780 | one will be created.
781 | :param use_cookies: Persist cookies from ``Set-Cookie`` response headers to the
782 | ``Cookie`` header in subsequent requests. Domain and path matching is supported,
783 | but other cookie parameters are ignored.
784 | :param allow_subdomain_redirects: Allow requests to follow redirects to subdomains.
785 | Enable this if the application handles subdomains and redirects between them.
786 |
787 | .. versionchanged:: 2.3
788 | Simplify cookie implementation, support domain and path matching.
789 |
790 | .. versionchanged:: 2.1
791 | All data is available as properties on the returned response object. The
792 | response cannot be returned as a tuple.
793 |
794 | .. versionchanged:: 2.0
795 | ``response_wrapper`` is always a subclass of :class:``TestResponse``.
796 |
797 | .. versionchanged:: 0.5
798 | Added the ``use_cookies`` parameter.
799 | """
800 |
801 | def __init__(
802 | self,
803 | application: WSGIApplication,
804 | response_wrapper: type[Response] | None = None,
805 | use_cookies: bool = True,
806 | allow_subdomain_redirects: bool = False,
807 | ) -> None:
808 | self.application = application
809 |
810 | if response_wrapper in {None, Response}:
811 | response_wrapper = TestResponse
812 | elif response_wrapper is not None and not issubclass(
813 | response_wrapper, TestResponse
814 | ):
815 | response_wrapper = type(
816 | "WrapperTestResponse",
817 | (TestResponse, response_wrapper),
818 | {},
819 | )
820 |
821 | self.response_wrapper = t.cast(t.Type["TestResponse"], response_wrapper)
822 |
823 | if use_cookies:
824 | self._cookies: dict[tuple[str, str, str], Cookie] | None = {}
825 | else:
826 | self._cookies = None
827 |
828 | self.allow_subdomain_redirects = allow_subdomain_redirects
829 |
830 | def get_cookie(
831 | self, key: str, domain: str = "localhost", path: str = "/"
832 | ) -> Cookie | None:
833 | """Return a :class:`.Cookie` if it exists. Cookies are uniquely identified by
834 | ``(domain, path, key)``.
835 |
836 | :param key: The decoded form of the key for the cookie.
837 | :param domain: The domain the cookie was set for.
838 | :param path: The path the cookie was set for.
839 |
840 | .. versionadded:: 2.3
841 | """
842 | if self._cookies is None:
843 | raise TypeError(
844 | "Cookies are disabled. Create a client with 'use_cookies=True'."
845 | )
846 |
847 | return self._cookies.get((domain, path, key))
848 |
849 | def set_cookie(
850 | self,
851 | key: str,
852 | value: str = "",
853 | *,
854 | domain: str = "localhost",
855 | origin_only: bool = True,
856 | path: str = "/",
857 | **kwargs: t.Any,
858 | ) -> None:
859 | """Set a cookie to be sent in subsequent requests.
860 |
861 | This is a convenience to skip making a test request to a route that would set
862 | the cookie. To test the cookie, make a test request to a route that uses the
863 | cookie value.
864 |
865 | The client uses ``domain``, ``origin_only``, and ``path`` to determine which
866 | cookies to send with a request. It does not use other cookie parameters that
867 | browsers use, since they're not applicable in tests.
868 |
869 | :param key: The key part of the cookie.
870 | :param value: The value part of the cookie.
871 | :param domain: Send this cookie with requests that match this domain. If
872 | ``origin_only`` is true, it must be an exact match, otherwise it may be a
873 | suffix match.
874 | :param origin_only: Whether the domain must be an exact match to the request.
875 | :param path: Send this cookie with requests that match this path either exactly
876 | or as a prefix.
877 | :param kwargs: Passed to :func:`.dump_cookie`.
878 |
879 | .. versionchanged:: 3.0
880 | The parameter ``server_name`` is removed. The first parameter is
881 | ``key``. Use the ``domain`` and ``origin_only`` parameters instead.
882 |
883 | .. versionchanged:: 2.3
884 | The ``origin_only`` parameter was added.
885 |
886 | .. versionchanged:: 2.3
887 | The ``domain`` parameter defaults to ``localhost``.
888 | """
889 | if self._cookies is None:
890 | raise TypeError(
891 | "Cookies are disabled. Create a client with 'use_cookies=True'."
892 | )
893 |
894 | cookie = Cookie._from_response_header(
895 | domain, "/", dump_cookie(key, value, domain=domain, path=path, **kwargs)
896 | )
897 | cookie.origin_only = origin_only
898 |
899 | if cookie._should_delete:
900 | self._cookies.pop(cookie._storage_key, None)
901 | else:
902 | self._cookies[cookie._storage_key] = cookie
903 |
904 | def delete_cookie(
905 | self,
906 | key: str,
907 | *,
908 | domain: str = "localhost",
909 | path: str = "/",
910 | ) -> None:
911 | """Delete a cookie if it exists. Cookies are uniquely identified by
912 | ``(domain, path, key)``.
913 |
914 | :param key: The decoded form of the key for the cookie.
915 | :param domain: The domain the cookie was set for.
916 | :param path: The path the cookie was set for.
917 |
918 | .. versionchanged:: 3.0
919 | The ``server_name`` parameter is removed. The first parameter is
920 | ``key``. Use the ``domain`` parameter instead.
921 |
922 | .. versionchanged:: 3.0
923 | The ``secure``, ``httponly`` and ``samesite`` parameters are removed.
924 |
925 | .. versionchanged:: 2.3
926 | The ``domain`` parameter defaults to ``localhost``.
927 | """
928 | if self._cookies is None:
929 | raise TypeError(
930 | "Cookies are disabled. Create a client with 'use_cookies=True'."
931 | )
932 |
933 | self._cookies.pop((domain, path, key), None)
934 |
935 | def _add_cookies_to_wsgi(self, environ: WSGIEnvironment) -> None:
936 | """If cookies are enabled, set the ``Cookie`` header in the environ to the
937 | cookies that are applicable to the request host and path.
938 |
939 | :meta private:
940 |
941 | .. versionadded:: 2.3
942 | """
943 | if self._cookies is None:
944 | return
945 |
946 | url = urlsplit(get_current_url(environ))
947 | server_name = url.hostname or "localhost"
948 | value = "; ".join(
949 | c._to_request_header()
950 | for c in self._cookies.values()
951 | if c._matches_request(server_name, url.path)
952 | )
953 |
954 | if value:
955 | environ["HTTP_COOKIE"] = value
956 | else:
957 | environ.pop("HTTP_COOKIE", None)
958 |
959 | def _update_cookies_from_response(
960 | self, server_name: str, path: str, headers: list[str]
961 | ) -> None:
962 | """If cookies are enabled, update the stored cookies from any ``Set-Cookie``
963 | headers in the response.
964 |
965 | :meta private:
966 |
967 | .. versionadded:: 2.3
968 | """
969 | if self._cookies is None:
970 | return
971 |
972 | for header in headers:
973 | cookie = Cookie._from_response_header(server_name, path, header)
974 |
975 | if cookie._should_delete:
976 | self._cookies.pop(cookie._storage_key, None)
977 | else:
978 | self._cookies[cookie._storage_key] = cookie
979 |
980 | def run_wsgi_app(
981 | self, environ: WSGIEnvironment, buffered: bool = False
982 | ) -> tuple[t.Iterable[bytes], str, Headers]:
983 | """Runs the wrapped WSGI app with the given environment.
984 |
985 | :meta private:
986 | """
987 | self._add_cookies_to_wsgi(environ)
988 | rv = run_wsgi_app(self.application, environ, buffered=buffered)
989 | url = urlsplit(get_current_url(environ))
990 | self._update_cookies_from_response(
991 | url.hostname or "localhost", url.path, rv[2].getlist("Set-Cookie")
992 | )
993 | return rv
994 |
995 | def resolve_redirect(
996 | self, response: TestResponse, buffered: bool = False
997 | ) -> TestResponse:
998 | """Perform a new request to the location given by the redirect
999 | response to the previous request.
1000 |
1001 | :meta private:
1002 | """
1003 | scheme, netloc, path, qs, anchor = urlsplit(response.location)
1004 | builder = EnvironBuilder.from_environ(
1005 | response.request.environ, path=path, query_string=qs
1006 | )
1007 |
1008 | to_name_parts = netloc.split(":", 1)[0].split(".")
1009 | from_name_parts = builder.server_name.split(".")
1010 |
1011 | if to_name_parts != [""]:
1012 | # The new location has a host, use it for the base URL.
1013 | builder.url_scheme = scheme
1014 | builder.host = netloc
1015 | else:
1016 | # A local redirect with autocorrect_location_header=False
1017 | # doesn't have a host, so use the request's host.
1018 | to_name_parts = from_name_parts
1019 |
1020 | # Explain why a redirect to a different server name won't be followed.
1021 | if to_name_parts != from_name_parts:
1022 | if to_name_parts[-len(from_name_parts) :] == from_name_parts:
1023 | if not self.allow_subdomain_redirects:
1024 | raise RuntimeError("Following subdomain redirects is not enabled.")
1025 | else:
1026 | raise RuntimeError("Following external redirects is not supported.")
1027 |
1028 | path_parts = path.split("/")
1029 | root_parts = builder.script_root.split("/")
1030 |
1031 | if path_parts[: len(root_parts)] == root_parts:
1032 | # Strip the script root from the path.
1033 | builder.path = path[len(builder.script_root) :]
1034 | else:
1035 | # The new location is not under the script root, so use the
1036 | # whole path and clear the previous root.
1037 | builder.path = path
1038 | builder.script_root = ""
1039 |
1040 | # Only 307 and 308 preserve all of the original request.
1041 | if response.status_code not in {307, 308}:
1042 | # HEAD is preserved, everything else becomes GET.
1043 | if builder.method != "HEAD":
1044 | builder.method = "GET"
1045 |
1046 | # Clear the body and the headers that describe it.
1047 |
1048 | if builder.input_stream is not None:
1049 | builder.input_stream.close()
1050 | builder.input_stream = None
1051 |
1052 | builder.content_type = None
1053 | builder.content_length = None
1054 | builder.headers.pop("Transfer-Encoding", None)
1055 |
1056 | return self.open(builder, buffered=buffered)
1057 |
1058 | def open(
1059 | self,
1060 | *args: t.Any,
1061 | buffered: bool = False,
1062 | follow_redirects: bool = False,
1063 | **kwargs: t.Any,
1064 | ) -> TestResponse:
1065 | """Generate an environ dict from the given arguments, make a
1066 | request to the application using it, and return the response.
1067 |
1068 | :param args: Passed to :class:`EnvironBuilder` to create the
1069 | environ for the request. If a single arg is passed, it can
1070 | be an existing :class:`EnvironBuilder` or an environ dict.
1071 | :param buffered: Convert the iterator returned by the app into
1072 | a list. If the iterator has a ``close()`` method, it is
1073 | called automatically.
1074 | :param follow_redirects: Make additional requests to follow HTTP
1075 | redirects until a non-redirect status is returned.
1076 | :attr:`TestResponse.history` lists the intermediate
1077 | responses.
1078 |
1079 | .. versionchanged:: 2.1
1080 | Removed the ``as_tuple`` parameter.
1081 |
1082 | .. versionchanged:: 2.0
1083 | The request input stream is closed when calling
1084 | ``response.close()``. Input streams for redirects are
1085 | automatically closed.
1086 |
1087 | .. versionchanged:: 0.5
1088 | If a dict is provided as file in the dict for the ``data``
1089 | parameter the content type has to be called ``content_type``
1090 | instead of ``mimetype``. This change was made for
1091 | consistency with :class:`werkzeug.FileWrapper`.
1092 |
1093 | .. versionchanged:: 0.5
1094 | Added the ``follow_redirects`` parameter.
1095 | """
1096 | request: Request | None = None
1097 |
1098 | if not kwargs and len(args) == 1:
1099 | arg = args[0]
1100 |
1101 | if isinstance(arg, EnvironBuilder):
1102 | request = arg.get_request()
1103 | elif isinstance(arg, dict):
1104 | request = EnvironBuilder.from_environ(arg).get_request()
1105 | elif isinstance(arg, Request):
1106 | request = arg
1107 |
1108 | if request is None:
1109 | builder = EnvironBuilder(*args, **kwargs)
1110 |
1111 | try:
1112 | request = builder.get_request()
1113 | finally:
1114 | builder.close()
1115 |
1116 | response_parts = self.run_wsgi_app(request.environ, buffered=buffered)
1117 | response = self.response_wrapper(*response_parts, request=request)
1118 |
1119 | redirects = set()
1120 | history: list[TestResponse] = []
1121 |
1122 | if not follow_redirects:
1123 | return response
1124 |
1125 | while response.status_code in {
1126 | 301,
1127 | 302,
1128 | 303,
1129 | 305,
1130 | 307,
1131 | 308,
1132 | }:
1133 | # Exhaust intermediate response bodies to ensure middleware
1134 | # that returns an iterator runs any cleanup code.
1135 | if not buffered:
1136 | response.make_sequence()
1137 | response.close()
1138 |
1139 | new_redirect_entry = (response.location, response.status_code)
1140 |
1141 | if new_redirect_entry in redirects:
1142 | raise ClientRedirectError(
1143 | f"Loop detected: A {response.status_code} redirect"
1144 | f" to {response.location} was already made."
1145 | )
1146 |
1147 | redirects.add(new_redirect_entry)
1148 | response.history = tuple(history)
1149 | history.append(response)
1150 | response = self.resolve_redirect(response, buffered=buffered)
1151 | else:
1152 | # This is the final request after redirects.
1153 | response.history = tuple(history)
1154 | # Close the input stream when closing the response, in case
1155 | # the input is an open temporary file.
1156 | response.call_on_close(request.input_stream.close)
1157 | return response
1158 |
1159 | def get(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1160 | """Call :meth:`open` with ``method`` set to ``GET``."""
1161 | kw["method"] = "GET"
1162 | return self.open(*args, **kw)
1163 |
1164 | def post(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1165 | """Call :meth:`open` with ``method`` set to ``POST``."""
1166 | kw["method"] = "POST"
1167 | return self.open(*args, **kw)
1168 |
1169 | def put(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1170 | """Call :meth:`open` with ``method`` set to ``PUT``."""
1171 | kw["method"] = "PUT"
1172 | return self.open(*args, **kw)
1173 |
1174 | def delete(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1175 | """Call :meth:`open` with ``method`` set to ``DELETE``."""
1176 | kw["method"] = "DELETE"
1177 | return self.open(*args, **kw)
1178 |
1179 | def patch(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1180 | """Call :meth:`open` with ``method`` set to ``PATCH``."""
1181 | kw["method"] = "PATCH"
1182 | return self.open(*args, **kw)
1183 |
1184 | def options(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1185 | """Call :meth:`open` with ``method`` set to ``OPTIONS``."""
1186 | kw["method"] = "OPTIONS"
1187 | return self.open(*args, **kw)
1188 |
1189 | def head(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1190 | """Call :meth:`open` with ``method`` set to ``HEAD``."""
1191 | kw["method"] = "HEAD"
1192 | return self.open(*args, **kw)
1193 |
1194 | def trace(self, *args: t.Any, **kw: t.Any) -> TestResponse:
1195 | """Call :meth:`open` with ``method`` set to ``TRACE``."""
1196 | kw["method"] = "TRACE"
1197 | return self.open(*args, **kw)
1198 |
1199 | def __repr__(self) -> str:
1200 | return f"<{type(self).__name__} {self.application!r}>"
1201 |
1202 |
1203 | def create_environ(*args: t.Any, **kwargs: t.Any) -> WSGIEnvironment:
1204 | """Create a new WSGI environ dict based on the values passed. The first
1205 | parameter should be the path of the request which defaults to '/'. The
1206 | second one can either be an absolute path (in that case the host is
1207 | localhost:80) or a full path to the request with scheme, netloc port and
1208 | the path to the script.
1209 |
1210 | This accepts the same arguments as the :class:`EnvironBuilder`
1211 | constructor.
1212 |
1213 | .. versionchanged:: 0.5
1214 | This function is now a thin wrapper over :class:`EnvironBuilder` which
1215 | was added in 0.5. The `headers`, `environ_base`, `environ_overrides`
1216 | and `charset` parameters were added.
1217 | """
1218 | builder = EnvironBuilder(*args, **kwargs)
1219 |
1220 | try:
1221 | return builder.get_environ()
1222 | finally:
1223 | builder.close()
1224 |
1225 |
1226 | def run_wsgi_app(
1227 | app: WSGIApplication, environ: WSGIEnvironment, buffered: bool = False
1228 | ) -> tuple[t.Iterable[bytes], str, Headers]:
1229 | """Return a tuple in the form (app_iter, status, headers) of the
1230 | application output. This works best if you pass it an application that
1231 | returns an iterator all the time.
1232 |
1233 | Sometimes applications may use the `write()` callable returned
1234 | by the `start_response` function. This tries to resolve such edge
1235 | cases automatically. But if you don't get the expected output you
1236 | should set `buffered` to `True` which enforces buffering.
1237 |
1238 | If passed an invalid WSGI application the behavior of this function is
1239 | undefined. Never pass non-conforming WSGI applications to this function.
1240 |
1241 | :param app: the application to execute.
1242 | :param buffered: set to `True` to enforce buffering.
1243 | :return: tuple in the form ``(app_iter, status, headers)``
1244 | """
1245 | # Copy environ to ensure any mutations by the app (ProxyFix, for
1246 | # example) don't affect subsequent requests (such as redirects).
1247 | environ = _get_environ(environ).copy()
1248 | status: str
1249 | response: tuple[str, list[tuple[str, str]]] | None = None
1250 | buffer: list[bytes] = []
1251 |
1252 | def start_response(status, headers, exc_info=None): # type: ignore
1253 | nonlocal response
1254 |
1255 | if exc_info:
1256 | try:
1257 | raise exc_info[1].with_traceback(exc_info[2])
1258 | finally:
1259 | exc_info = None
1260 |
1261 | response = (status, headers)
1262 | return buffer.append
1263 |
1264 | app_rv = app(environ, start_response)
1265 | close_func = getattr(app_rv, "close", None)
1266 | app_iter: t.Iterable[bytes] = iter(app_rv)
1267 |
1268 | # when buffering we emit the close call early and convert the
1269 | # application iterator into a regular list
1270 | if buffered:
1271 | try:
1272 | app_iter = list(app_iter)
1273 | finally:
1274 | if close_func is not None:
1275 | close_func()
1276 |
1277 | # otherwise we iterate the application iter until we have a response, chain
1278 | # the already received data with the already collected data and wrap it in
1279 | # a new `ClosingIterator` if we need to restore a `close` callable from the
1280 | # original return value.
1281 | else:
1282 | for item in app_iter:
1283 | buffer.append(item)
1284 |
1285 | if response is not None:
1286 | break
1287 |
1288 | if buffer:
1289 | app_iter = chain(buffer, app_iter)
1290 |
1291 | if close_func is not None and app_iter is not app_rv:
1292 | app_iter = ClosingIterator(app_iter, close_func)
1293 |
1294 | status, headers = response # type: ignore
1295 | return app_iter, status, Headers(headers)
1296 |
1297 |
1298 | class TestResponse(Response):
1299 | """:class:`~werkzeug.wrappers.Response` subclass that provides extra
1300 | information about requests made with the test :class:`Client`.
1301 |
1302 | Test client requests will always return an instance of this class.
1303 | If a custom response class is passed to the client, it is
1304 | subclassed along with this to support test information.
1305 |
1306 | If the test request included large files, or if the application is
1307 | serving a file, call :meth:`close` to close any open files and
1308 | prevent Python showing a ``ResourceWarning``.
1309 |
1310 | .. versionchanged:: 2.2
1311 | Set the ``default_mimetype`` to None to prevent a mimetype being
1312 | assumed if missing.
1313 |
1314 | .. versionchanged:: 2.1
1315 | Response instances cannot be treated as tuples.
1316 |
1317 | .. versionadded:: 2.0
1318 | Test client methods always return instances of this class.
1319 | """
1320 |
1321 | default_mimetype = None
1322 | # Don't assume a mimetype, instead use whatever the response provides
1323 |
1324 | request: Request
1325 | """A request object with the environ used to make the request that
1326 | resulted in this response.
1327 | """
1328 |
1329 | history: tuple[TestResponse, ...]
1330 | """A list of intermediate responses. Populated when the test request
1331 | is made with ``follow_redirects`` enabled.
1332 | """
1333 |
1334 | # Tell Pytest to ignore this, it's not a test class.
1335 | __test__ = False
1336 |
1337 | def __init__(
1338 | self,
1339 | response: t.Iterable[bytes],
1340 | status: str,
1341 | headers: Headers,
1342 | request: Request,
1343 | history: tuple[TestResponse] = (), # type: ignore
1344 | **kwargs: t.Any,
1345 | ) -> None:
1346 | super().__init__(response, status, headers, **kwargs)
1347 | self.request = request
1348 | self.history = history
1349 | self._compat_tuple = response, status, headers
1350 |
1351 | @cached_property
1352 | def text(self) -> str:
1353 | """The response data as text. A shortcut for
1354 | ``response.get_data(as_text=True)``.
1355 |
1356 | .. versionadded:: 2.1
1357 | """
1358 | return self.get_data(as_text=True)
1359 |
1360 |
1361 | @dataclasses.dataclass
1362 | class Cookie:
1363 | """A cookie key, value, and parameters.
1364 |
1365 | The class itself is not a public API. Its attributes are documented for inspection
1366 | with :meth:`.Client.get_cookie` only.
1367 |
1368 | .. versionadded:: 2.3
1369 | """
1370 |
1371 | key: str
1372 | """The cookie key, encoded as a client would see it."""
1373 |
1374 | value: str
1375 | """The cookie key, encoded as a client would see it."""
1376 |
1377 | decoded_key: str
1378 | """The cookie key, decoded as the application would set and see it."""
1379 |
1380 | decoded_value: str
1381 | """The cookie value, decoded as the application would set and see it."""
1382 |
1383 | expires: datetime | None
1384 | """The time at which the cookie is no longer valid."""
1385 |
1386 | max_age: int | None
1387 | """The number of seconds from when the cookie was set at which it is
1388 | no longer valid.
1389 | """
1390 |
1391 | domain: str
1392 | """The domain that the cookie was set for, or the request domain if not set."""
1393 |
1394 | origin_only: bool
1395 | """Whether the cookie will be sent for exact domain matches only. This is ``True``
1396 | if the ``Domain`` parameter was not present.
1397 | """
1398 |
1399 | path: str
1400 | """The path that the cookie was set for."""
1401 |
1402 | secure: bool | None
1403 | """The ``Secure`` parameter."""
1404 |
1405 | http_only: bool | None
1406 | """The ``HttpOnly`` parameter."""
1407 |
1408 | same_site: str | None
1409 | """The ``SameSite`` parameter."""
1410 |
1411 | def _matches_request(self, server_name: str, path: str) -> bool:
1412 | return (
1413 | server_name == self.domain
1414 | or (
1415 | not self.origin_only
1416 | and server_name.endswith(self.domain)
1417 | and server_name[: -len(self.domain)].endswith(".")
1418 | )
1419 | ) and (
1420 | path == self.path
1421 | or (
1422 | path.startswith(self.path)
1423 | and path[len(self.path) - self.path.endswith("/") :].startswith("/")
1424 | )
1425 | )
1426 |
1427 | def _to_request_header(self) -> str:
1428 | return f"{self.key}={self.value}"
1429 |
1430 | @classmethod
1431 | def _from_response_header(cls, server_name: str, path: str, header: str) -> te.Self:
1432 | header, _, parameters_str = header.partition(";")
1433 | key, _, value = header.partition("=")
1434 | decoded_key, decoded_value = next(parse_cookie(header).items())
1435 | params = {}
1436 |
1437 | for item in parameters_str.split(";"):
1438 | k, sep, v = item.partition("=")
1439 | params[k.strip().lower()] = v.strip() if sep else None
1440 |
1441 | return cls(
1442 | key=key.strip(),
1443 | value=value.strip(),
1444 | decoded_key=decoded_key,
1445 | decoded_value=decoded_value,
1446 | expires=parse_date(params.get("expires")),
1447 | max_age=int(params["max-age"] or 0) if "max-age" in params else None,
1448 | domain=params.get("domain") or server_name,
1449 | origin_only="domain" not in params,
1450 | path=params.get("path") or path.rpartition("/")[0] or "/",
1451 | secure="secure" in params,
1452 | http_only="httponly" in params,
1453 | same_site=params.get("samesite"),
1454 | )
1455 |
1456 | @property
1457 | def _storage_key(self) -> tuple[str, str, str]:
1458 | return self.domain, self.path, self.decoded_key
1459 |
1460 | @property
1461 | def _should_delete(self) -> bool:
1462 | return self.max_age == 0 or (
1463 | self.expires is not None and self.expires.timestamp() == 0
1464 | )
1465 |
```