This is page 98 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/wrappers/response.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import json
4 | import typing as t
5 | from http import HTTPStatus
6 | from urllib.parse import urljoin
7 |
8 | from .._internal import _get_environ
9 | from ..datastructures import Headers
10 | from ..http import generate_etag
11 | from ..http import http_date
12 | from ..http import is_resource_modified
13 | from ..http import parse_etags
14 | from ..http import parse_range_header
15 | from ..http import remove_entity_headers
16 | from ..sansio.response import Response as _SansIOResponse
17 | from ..urls import iri_to_uri
18 | from ..utils import cached_property
19 | from ..wsgi import _RangeWrapper
20 | from ..wsgi import ClosingIterator
21 | from ..wsgi import get_current_url
22 |
23 | if t.TYPE_CHECKING:
24 | from _typeshed.wsgi import StartResponse
25 | from _typeshed.wsgi import WSGIApplication
26 | from _typeshed.wsgi import WSGIEnvironment
27 |
28 | from .request import Request
29 |
30 |
31 | def _iter_encoded(iterable: t.Iterable[str | bytes]) -> t.Iterator[bytes]:
32 | for item in iterable:
33 | if isinstance(item, str):
34 | yield item.encode()
35 | else:
36 | yield item
37 |
38 |
39 | class Response(_SansIOResponse):
40 | """Represents an outgoing WSGI HTTP response with body, status, and
41 | headers. Has properties and methods for using the functionality
42 | defined by various HTTP specs.
43 |
44 | The response body is flexible to support different use cases. The
45 | simple form is passing bytes, or a string which will be encoded as
46 | UTF-8. Passing an iterable of bytes or strings makes this a
47 | streaming response. A generator is particularly useful for building
48 | a CSV file in memory or using SSE (Server Sent Events). A file-like
49 | object is also iterable, although the
50 | :func:`~werkzeug.utils.send_file` helper should be used in that
51 | case.
52 |
53 | The response object is itself a WSGI application callable. When
54 | called (:meth:`__call__`) with ``environ`` and ``start_response``,
55 | it will pass its status and headers to ``start_response`` then
56 | return its body as an iterable.
57 |
58 | .. code-block:: python
59 |
60 | from werkzeug.wrappers.response import Response
61 |
62 | def index():
63 | return Response("Hello, World!")
64 |
65 | def application(environ, start_response):
66 | path = environ.get("PATH_INFO") or "/"
67 |
68 | if path == "/":
69 | response = index()
70 | else:
71 | response = Response("Not Found", status=404)
72 |
73 | return response(environ, start_response)
74 |
75 | :param response: The data for the body of the response. A string or
76 | bytes, or tuple or list of strings or bytes, for a fixed-length
77 | response, or any other iterable of strings or bytes for a
78 | streaming response. Defaults to an empty body.
79 | :param status: The status code for the response. Either an int, in
80 | which case the default status message is added, or a string in
81 | the form ``{code} {message}``, like ``404 Not Found``. Defaults
82 | to 200.
83 | :param headers: A :class:`~werkzeug.datastructures.Headers` object,
84 | or a list of ``(key, value)`` tuples that will be converted to a
85 | ``Headers`` object.
86 | :param mimetype: The mime type (content type without charset or
87 | other parameters) of the response. If the value starts with
88 | ``text/`` (or matches some other special cases), the charset
89 | will be added to create the ``content_type``.
90 | :param content_type: The full content type of the response.
91 | Overrides building the value from ``mimetype``.
92 | :param direct_passthrough: Pass the response body directly through
93 | as the WSGI iterable. This can be used when the body is a binary
94 | file or other iterator of bytes, to skip some unnecessary
95 | checks. Use :func:`~werkzeug.utils.send_file` instead of setting
96 | this manually.
97 |
98 | .. versionchanged:: 2.1
99 | Old ``BaseResponse`` and mixin classes were removed.
100 |
101 | .. versionchanged:: 2.0
102 | Combine ``BaseResponse`` and mixins into a single ``Response``
103 | class.
104 |
105 | .. versionchanged:: 0.5
106 | The ``direct_passthrough`` parameter was added.
107 | """
108 |
109 | #: if set to `False` accessing properties on the response object will
110 | #: not try to consume the response iterator and convert it into a list.
111 | #:
112 | #: .. versionadded:: 0.6.2
113 | #:
114 | #: That attribute was previously called `implicit_seqence_conversion`.
115 | #: (Notice the typo). If you did use this feature, you have to adapt
116 | #: your code to the name change.
117 | implicit_sequence_conversion = True
118 |
119 | #: If a redirect ``Location`` header is a relative URL, make it an
120 | #: absolute URL, including scheme and domain.
121 | #:
122 | #: .. versionchanged:: 2.1
123 | #: This is disabled by default, so responses will send relative
124 | #: redirects.
125 | #:
126 | #: .. versionadded:: 0.8
127 | autocorrect_location_header = False
128 |
129 | #: Should this response object automatically set the content-length
130 | #: header if possible? This is true by default.
131 | #:
132 | #: .. versionadded:: 0.8
133 | automatically_set_content_length = True
134 |
135 | #: The response body to send as the WSGI iterable. A list of strings
136 | #: or bytes represents a fixed-length response, any other iterable
137 | #: is a streaming response. Strings are encoded to bytes as UTF-8.
138 | #:
139 | #: Do not set to a plain string or bytes, that will cause sending
140 | #: the response to be very inefficient as it will iterate one byte
141 | #: at a time.
142 | response: t.Iterable[str] | t.Iterable[bytes]
143 |
144 | def __init__(
145 | self,
146 | response: t.Iterable[bytes] | bytes | t.Iterable[str] | str | None = None,
147 | status: int | str | HTTPStatus | None = None,
148 | headers: t.Mapping[str, str | t.Iterable[str]]
149 | | t.Iterable[tuple[str, str]]
150 | | None = None,
151 | mimetype: str | None = None,
152 | content_type: str | None = None,
153 | direct_passthrough: bool = False,
154 | ) -> None:
155 | super().__init__(
156 | status=status,
157 | headers=headers,
158 | mimetype=mimetype,
159 | content_type=content_type,
160 | )
161 |
162 | #: Pass the response body directly through as the WSGI iterable.
163 | #: This can be used when the body is a binary file or other
164 | #: iterator of bytes, to skip some unnecessary checks. Use
165 | #: :func:`~werkzeug.utils.send_file` instead of setting this
166 | #: manually.
167 | self.direct_passthrough = direct_passthrough
168 | self._on_close: list[t.Callable[[], t.Any]] = []
169 |
170 | # we set the response after the headers so that if a class changes
171 | # the charset attribute, the data is set in the correct charset.
172 | if response is None:
173 | self.response = []
174 | elif isinstance(response, (str, bytes, bytearray)):
175 | self.set_data(response)
176 | else:
177 | self.response = response
178 |
179 | def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]:
180 | """Adds a function to the internal list of functions that should
181 | be called as part of closing down the response. Since 0.7 this
182 | function also returns the function that was passed so that this
183 | can be used as a decorator.
184 |
185 | .. versionadded:: 0.6
186 | """
187 | self._on_close.append(func)
188 | return func
189 |
190 | def __repr__(self) -> str:
191 | if self.is_sequence:
192 | body_info = f"{sum(map(len, self.iter_encoded()))} bytes"
193 | else:
194 | body_info = "streamed" if self.is_streamed else "likely-streamed"
195 | return f"<{type(self).__name__} {body_info} [{self.status}]>"
196 |
197 | @classmethod
198 | def force_type(
199 | cls, response: Response, environ: WSGIEnvironment | None = None
200 | ) -> Response:
201 | """Enforce that the WSGI response is a response object of the current
202 | type. Werkzeug will use the :class:`Response` internally in many
203 | situations like the exceptions. If you call :meth:`get_response` on an
204 | exception you will get back a regular :class:`Response` object, even
205 | if you are using a custom subclass.
206 |
207 | This method can enforce a given response type, and it will also
208 | convert arbitrary WSGI callables into response objects if an environ
209 | is provided::
210 |
211 | # convert a Werkzeug response object into an instance of the
212 | # MyResponseClass subclass.
213 | response = MyResponseClass.force_type(response)
214 |
215 | # convert any WSGI application into a response object
216 | response = MyResponseClass.force_type(response, environ)
217 |
218 | This is especially useful if you want to post-process responses in
219 | the main dispatcher and use functionality provided by your subclass.
220 |
221 | Keep in mind that this will modify response objects in place if
222 | possible!
223 |
224 | :param response: a response object or wsgi application.
225 | :param environ: a WSGI environment object.
226 | :return: a response object.
227 | """
228 | if not isinstance(response, Response):
229 | if environ is None:
230 | raise TypeError(
231 | "cannot convert WSGI application into response"
232 | " objects without an environ"
233 | )
234 |
235 | from ..test import run_wsgi_app
236 |
237 | response = Response(*run_wsgi_app(response, environ))
238 |
239 | response.__class__ = cls
240 | return response
241 |
242 | @classmethod
243 | def from_app(
244 | cls, app: WSGIApplication, environ: WSGIEnvironment, buffered: bool = False
245 | ) -> Response:
246 | """Create a new response object from an application output. This
247 | works best if you pass it an application that returns a generator all
248 | the time. Sometimes applications may use the `write()` callable
249 | returned by the `start_response` function. This tries to resolve such
250 | edge cases automatically. But if you don't get the expected output
251 | you should set `buffered` to `True` which enforces buffering.
252 |
253 | :param app: the WSGI application to execute.
254 | :param environ: the WSGI environment to execute against.
255 | :param buffered: set to `True` to enforce buffering.
256 | :return: a response object.
257 | """
258 | from ..test import run_wsgi_app
259 |
260 | return cls(*run_wsgi_app(app, environ, buffered))
261 |
262 | @t.overload
263 | def get_data(self, as_text: t.Literal[False] = False) -> bytes: ...
264 |
265 | @t.overload
266 | def get_data(self, as_text: t.Literal[True]) -> str: ...
267 |
268 | def get_data(self, as_text: bool = False) -> bytes | str:
269 | """The string representation of the response body. Whenever you call
270 | this property the response iterable is encoded and flattened. This
271 | can lead to unwanted behavior if you stream big data.
272 |
273 | This behavior can be disabled by setting
274 | :attr:`implicit_sequence_conversion` to `False`.
275 |
276 | If `as_text` is set to `True` the return value will be a decoded
277 | string.
278 |
279 | .. versionadded:: 0.9
280 | """
281 | self._ensure_sequence()
282 | rv = b"".join(self.iter_encoded())
283 |
284 | if as_text:
285 | return rv.decode()
286 |
287 | return rv
288 |
289 | def set_data(self, value: bytes | str) -> None:
290 | """Sets a new string as response. The value must be a string or
291 | bytes. If a string is set it's encoded to the charset of the
292 | response (utf-8 by default).
293 |
294 | .. versionadded:: 0.9
295 | """
296 | if isinstance(value, str):
297 | value = value.encode()
298 | self.response = [value]
299 | if self.automatically_set_content_length:
300 | self.headers["Content-Length"] = str(len(value))
301 |
302 | data = property(
303 | get_data,
304 | set_data,
305 | doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.",
306 | )
307 |
308 | def calculate_content_length(self) -> int | None:
309 | """Returns the content length if available or `None` otherwise."""
310 | try:
311 | self._ensure_sequence()
312 | except RuntimeError:
313 | return None
314 | return sum(len(x) for x in self.iter_encoded())
315 |
316 | def _ensure_sequence(self, mutable: bool = False) -> None:
317 | """This method can be called by methods that need a sequence. If
318 | `mutable` is true, it will also ensure that the response sequence
319 | is a standard Python list.
320 |
321 | .. versionadded:: 0.6
322 | """
323 | if self.is_sequence:
324 | # if we need a mutable object, we ensure it's a list.
325 | if mutable and not isinstance(self.response, list):
326 | self.response = list(self.response) # type: ignore
327 | return
328 | if self.direct_passthrough:
329 | raise RuntimeError(
330 | "Attempted implicit sequence conversion but the"
331 | " response object is in direct passthrough mode."
332 | )
333 | if not self.implicit_sequence_conversion:
334 | raise RuntimeError(
335 | "The response object required the iterable to be a"
336 | " sequence, but the implicit conversion was disabled."
337 | " Call make_sequence() yourself."
338 | )
339 | self.make_sequence()
340 |
341 | def make_sequence(self) -> None:
342 | """Converts the response iterator in a list. By default this happens
343 | automatically if required. If `implicit_sequence_conversion` is
344 | disabled, this method is not automatically called and some properties
345 | might raise exceptions. This also encodes all the items.
346 |
347 | .. versionadded:: 0.6
348 | """
349 | if not self.is_sequence:
350 | # if we consume an iterable we have to ensure that the close
351 | # method of the iterable is called if available when we tear
352 | # down the response
353 | close = getattr(self.response, "close", None)
354 | self.response = list(self.iter_encoded())
355 | if close is not None:
356 | self.call_on_close(close)
357 |
358 | def iter_encoded(self) -> t.Iterator[bytes]:
359 | """Iter the response encoded with the encoding of the response.
360 | If the response object is invoked as WSGI application the return
361 | value of this method is used as application iterator unless
362 | :attr:`direct_passthrough` was activated.
363 | """
364 | # Encode in a separate function so that self.response is fetched
365 | # early. This allows us to wrap the response with the return
366 | # value from get_app_iter or iter_encoded.
367 | return _iter_encoded(self.response)
368 |
369 | @property
370 | def is_streamed(self) -> bool:
371 | """If the response is streamed (the response is not an iterable with
372 | a length information) this property is `True`. In this case streamed
373 | means that there is no information about the number of iterations.
374 | This is usually `True` if a generator is passed to the response object.
375 |
376 | This is useful for checking before applying some sort of post
377 | filtering that should not take place for streamed responses.
378 | """
379 | try:
380 | len(self.response) # type: ignore
381 | except (TypeError, AttributeError):
382 | return True
383 | return False
384 |
385 | @property
386 | def is_sequence(self) -> bool:
387 | """If the iterator is buffered, this property will be `True`. A
388 | response object will consider an iterator to be buffered if the
389 | response attribute is a list or tuple.
390 |
391 | .. versionadded:: 0.6
392 | """
393 | return isinstance(self.response, (tuple, list))
394 |
395 | def close(self) -> None:
396 | """Close the wrapped response if possible. You can also use the object
397 | in a with statement which will automatically close it.
398 |
399 | .. versionadded:: 0.9
400 | Can now be used in a with statement.
401 | """
402 | if hasattr(self.response, "close"):
403 | self.response.close()
404 | for func in self._on_close:
405 | func()
406 |
407 | def __enter__(self) -> Response:
408 | return self
409 |
410 | def __exit__(self, exc_type, exc_value, tb): # type: ignore
411 | self.close()
412 |
413 | def freeze(self) -> None:
414 | """Make the response object ready to be pickled. Does the
415 | following:
416 |
417 | * Buffer the response into a list, ignoring
418 | :attr:`implicity_sequence_conversion` and
419 | :attr:`direct_passthrough`.
420 | * Set the ``Content-Length`` header.
421 | * Generate an ``ETag`` header if one is not already set.
422 |
423 | .. versionchanged:: 2.1
424 | Removed the ``no_etag`` parameter.
425 |
426 | .. versionchanged:: 2.0
427 | An ``ETag`` header is always added.
428 |
429 | .. versionchanged:: 0.6
430 | The ``Content-Length`` header is set.
431 | """
432 | # Always freeze the encoded response body, ignore
433 | # implicit_sequence_conversion and direct_passthrough.
434 | self.response = list(self.iter_encoded())
435 | self.headers["Content-Length"] = str(sum(map(len, self.response)))
436 | self.add_etag()
437 |
438 | def get_wsgi_headers(self, environ: WSGIEnvironment) -> Headers:
439 | """This is automatically called right before the response is started
440 | and returns headers modified for the given environment. It returns a
441 | copy of the headers from the response with some modifications applied
442 | if necessary.
443 |
444 | For example the location header (if present) is joined with the root
445 | URL of the environment. Also the content length is automatically set
446 | to zero here for certain status codes.
447 |
448 | .. versionchanged:: 0.6
449 | Previously that function was called `fix_headers` and modified
450 | the response object in place. Also since 0.6, IRIs in location
451 | and content-location headers are handled properly.
452 |
453 | Also starting with 0.6, Werkzeug will attempt to set the content
454 | length if it is able to figure it out on its own. This is the
455 | case if all the strings in the response iterable are already
456 | encoded and the iterable is buffered.
457 |
458 | :param environ: the WSGI environment of the request.
459 | :return: returns a new :class:`~werkzeug.datastructures.Headers`
460 | object.
461 | """
462 | headers = Headers(self.headers)
463 | location: str | None = None
464 | content_location: str | None = None
465 | content_length: str | int | None = None
466 | status = self.status_code
467 |
468 | # iterate over the headers to find all values in one go. Because
469 | # get_wsgi_headers is used each response that gives us a tiny
470 | # speedup.
471 | for key, value in headers:
472 | ikey = key.lower()
473 | if ikey == "location":
474 | location = value
475 | elif ikey == "content-location":
476 | content_location = value
477 | elif ikey == "content-length":
478 | content_length = value
479 |
480 | if location is not None:
481 | location = iri_to_uri(location)
482 |
483 | if self.autocorrect_location_header:
484 | # Make the location header an absolute URL.
485 | current_url = get_current_url(environ, strip_querystring=True)
486 | current_url = iri_to_uri(current_url)
487 | location = urljoin(current_url, location)
488 |
489 | headers["Location"] = location
490 |
491 | # make sure the content location is a URL
492 | if content_location is not None:
493 | headers["Content-Location"] = iri_to_uri(content_location)
494 |
495 | if 100 <= status < 200 or status == 204:
496 | # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a
497 | # Content-Length header field in any response with a status
498 | # code of 1xx (Informational) or 204 (No Content)."
499 | headers.remove("Content-Length")
500 | elif status == 304:
501 | remove_entity_headers(headers)
502 |
503 | # if we can determine the content length automatically, we
504 | # should try to do that. But only if this does not involve
505 | # flattening the iterator or encoding of strings in the
506 | # response. We however should not do that if we have a 304
507 | # response.
508 | if (
509 | self.automatically_set_content_length
510 | and self.is_sequence
511 | and content_length is None
512 | and status not in (204, 304)
513 | and not (100 <= status < 200)
514 | ):
515 | content_length = sum(len(x) for x in self.iter_encoded())
516 | headers["Content-Length"] = str(content_length)
517 |
518 | return headers
519 |
520 | def get_app_iter(self, environ: WSGIEnvironment) -> t.Iterable[bytes]:
521 | """Returns the application iterator for the given environ. Depending
522 | on the request method and the current status code the return value
523 | might be an empty response rather than the one from the response.
524 |
525 | If the request method is `HEAD` or the status code is in a range
526 | where the HTTP specification requires an empty response, an empty
527 | iterable is returned.
528 |
529 | .. versionadded:: 0.6
530 |
531 | :param environ: the WSGI environment of the request.
532 | :return: a response iterable.
533 | """
534 | status = self.status_code
535 | if (
536 | environ["REQUEST_METHOD"] == "HEAD"
537 | or 100 <= status < 200
538 | or status in (204, 304)
539 | ):
540 | iterable: t.Iterable[bytes] = ()
541 | elif self.direct_passthrough:
542 | return self.response # type: ignore
543 | else:
544 | iterable = self.iter_encoded()
545 | return ClosingIterator(iterable, self.close)
546 |
547 | def get_wsgi_response(
548 | self, environ: WSGIEnvironment
549 | ) -> tuple[t.Iterable[bytes], str, list[tuple[str, str]]]:
550 | """Returns the final WSGI response as tuple. The first item in
551 | the tuple is the application iterator, the second the status and
552 | the third the list of headers. The response returned is created
553 | specially for the given environment. For example if the request
554 | method in the WSGI environment is ``'HEAD'`` the response will
555 | be empty and only the headers and status code will be present.
556 |
557 | .. versionadded:: 0.6
558 |
559 | :param environ: the WSGI environment of the request.
560 | :return: an ``(app_iter, status, headers)`` tuple.
561 | """
562 | headers = self.get_wsgi_headers(environ)
563 | app_iter = self.get_app_iter(environ)
564 | return app_iter, self.status, headers.to_wsgi_list()
565 |
566 | def __call__(
567 | self, environ: WSGIEnvironment, start_response: StartResponse
568 | ) -> t.Iterable[bytes]:
569 | """Process this response as WSGI application.
570 |
571 | :param environ: the WSGI environment.
572 | :param start_response: the response callable provided by the WSGI
573 | server.
574 | :return: an application iterator
575 | """
576 | app_iter, status, headers = self.get_wsgi_response(environ)
577 | start_response(status, headers)
578 | return app_iter
579 |
580 | # JSON
581 |
582 | #: A module or other object that has ``dumps`` and ``loads``
583 | #: functions that match the API of the built-in :mod:`json` module.
584 | json_module = json
585 |
586 | @property
587 | def json(self) -> t.Any | None:
588 | """The parsed JSON data if :attr:`mimetype` indicates JSON
589 | (:mimetype:`application/json`, see :attr:`is_json`).
590 |
591 | Calls :meth:`get_json` with default arguments.
592 | """
593 | return self.get_json()
594 |
595 | @t.overload
596 | def get_json(self, force: bool = ..., silent: t.Literal[False] = ...) -> t.Any: ...
597 |
598 | @t.overload
599 | def get_json(self, force: bool = ..., silent: bool = ...) -> t.Any | None: ...
600 |
601 | def get_json(self, force: bool = False, silent: bool = False) -> t.Any | None:
602 | """Parse :attr:`data` as JSON. Useful during testing.
603 |
604 | If the mimetype does not indicate JSON
605 | (:mimetype:`application/json`, see :attr:`is_json`), this
606 | returns ``None``.
607 |
608 | Unlike :meth:`Request.get_json`, the result is not cached.
609 |
610 | :param force: Ignore the mimetype and always try to parse JSON.
611 | :param silent: Silence parsing errors and return ``None``
612 | instead.
613 | """
614 | if not (force or self.is_json):
615 | return None
616 |
617 | data = self.get_data()
618 |
619 | try:
620 | return self.json_module.loads(data)
621 | except ValueError:
622 | if not silent:
623 | raise
624 |
625 | return None
626 |
627 | # Stream
628 |
629 | @cached_property
630 | def stream(self) -> ResponseStream:
631 | """The response iterable as write-only stream."""
632 | return ResponseStream(self)
633 |
634 | def _wrap_range_response(self, start: int, length: int) -> None:
635 | """Wrap existing Response in case of Range Request context."""
636 | if self.status_code == 206:
637 | self.response = _RangeWrapper(self.response, start, length) # type: ignore
638 |
639 | def _is_range_request_processable(self, environ: WSGIEnvironment) -> bool:
640 | """Return ``True`` if `Range` header is present and if underlying
641 | resource is considered unchanged when compared with `If-Range` header.
642 | """
643 | return (
644 | "HTTP_IF_RANGE" not in environ
645 | or not is_resource_modified(
646 | environ,
647 | self.headers.get("etag"),
648 | None,
649 | self.headers.get("last-modified"),
650 | ignore_if_range=False,
651 | )
652 | ) and "HTTP_RANGE" in environ
653 |
654 | def _process_range_request(
655 | self,
656 | environ: WSGIEnvironment,
657 | complete_length: int | None,
658 | accept_ranges: bool | str,
659 | ) -> bool:
660 | """Handle Range Request related headers (RFC7233). If `Accept-Ranges`
661 | header is valid, and Range Request is processable, we set the headers
662 | as described by the RFC, and wrap the underlying response in a
663 | RangeWrapper.
664 |
665 | Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise.
666 |
667 | :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable`
668 | if `Range` header could not be parsed or satisfied.
669 |
670 | .. versionchanged:: 2.0
671 | Returns ``False`` if the length is 0.
672 | """
673 | from ..exceptions import RequestedRangeNotSatisfiable
674 |
675 | if (
676 | not accept_ranges
677 | or complete_length is None
678 | or complete_length == 0
679 | or not self._is_range_request_processable(environ)
680 | ):
681 | return False
682 |
683 | if accept_ranges is True:
684 | accept_ranges = "bytes"
685 |
686 | parsed_range = parse_range_header(environ.get("HTTP_RANGE"))
687 |
688 | if parsed_range is None:
689 | raise RequestedRangeNotSatisfiable(complete_length)
690 |
691 | range_tuple = parsed_range.range_for_length(complete_length)
692 | content_range_header = parsed_range.to_content_range_header(complete_length)
693 |
694 | if range_tuple is None or content_range_header is None:
695 | raise RequestedRangeNotSatisfiable(complete_length)
696 |
697 | content_length = range_tuple[1] - range_tuple[0]
698 | self.headers["Content-Length"] = str(content_length)
699 | self.headers["Accept-Ranges"] = accept_ranges
700 | self.content_range = content_range_header # type: ignore
701 | self.status_code = 206
702 | self._wrap_range_response(range_tuple[0], content_length)
703 | return True
704 |
705 | def make_conditional(
706 | self,
707 | request_or_environ: WSGIEnvironment | Request,
708 | accept_ranges: bool | str = False,
709 | complete_length: int | None = None,
710 | ) -> Response:
711 | """Make the response conditional to the request. This method works
712 | best if an etag was defined for the response already. The `add_etag`
713 | method can be used to do that. If called without etag just the date
714 | header is set.
715 |
716 | This does nothing if the request method in the request or environ is
717 | anything but GET or HEAD.
718 |
719 | For optimal performance when handling range requests, it's recommended
720 | that your response data object implements `seekable`, `seek` and `tell`
721 | methods as described by :py:class:`io.IOBase`. Objects returned by
722 | :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods.
723 |
724 | It does not remove the body of the response because that's something
725 | the :meth:`__call__` function does for us automatically.
726 |
727 | Returns self so that you can do ``return resp.make_conditional(req)``
728 | but modifies the object in-place.
729 |
730 | :param request_or_environ: a request object or WSGI environment to be
731 | used to make the response conditional
732 | against.
733 | :param accept_ranges: This parameter dictates the value of
734 | `Accept-Ranges` header. If ``False`` (default),
735 | the header is not set. If ``True``, it will be set
736 | to ``"bytes"``. If it's a string, it will use this
737 | value.
738 | :param complete_length: Will be used only in valid Range Requests.
739 | It will set `Content-Range` complete length
740 | value and compute `Content-Length` real value.
741 | This parameter is mandatory for successful
742 | Range Requests completion.
743 | :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable`
744 | if `Range` header could not be parsed or satisfied.
745 |
746 | .. versionchanged:: 2.0
747 | Range processing is skipped if length is 0 instead of
748 | raising a 416 Range Not Satisfiable error.
749 | """
750 | environ = _get_environ(request_or_environ)
751 | if environ["REQUEST_METHOD"] in ("GET", "HEAD"):
752 | # if the date is not in the headers, add it now. We however
753 | # will not override an already existing header. Unfortunately
754 | # this header will be overridden by many WSGI servers including
755 | # wsgiref.
756 | if "date" not in self.headers:
757 | self.headers["Date"] = http_date()
758 | is206 = self._process_range_request(environ, complete_length, accept_ranges)
759 | if not is206 and not is_resource_modified(
760 | environ,
761 | self.headers.get("etag"),
762 | None,
763 | self.headers.get("last-modified"),
764 | ):
765 | if parse_etags(environ.get("HTTP_IF_MATCH")):
766 | self.status_code = 412
767 | else:
768 | self.status_code = 304
769 | if (
770 | self.automatically_set_content_length
771 | and "content-length" not in self.headers
772 | ):
773 | length = self.calculate_content_length()
774 | if length is not None:
775 | self.headers["Content-Length"] = str(length)
776 | return self
777 |
778 | def add_etag(self, overwrite: bool = False, weak: bool = False) -> None:
779 | """Add an etag for the current response if there is none yet.
780 |
781 | .. versionchanged:: 2.0
782 | SHA-1 is used to generate the value. MD5 may not be
783 | available in some environments.
784 | """
785 | if overwrite or "etag" not in self.headers:
786 | self.set_etag(generate_etag(self.get_data()), weak)
787 |
788 |
789 | class ResponseStream:
790 | """A file descriptor like object used by :meth:`Response.stream` to
791 | represent the body of the stream. It directly pushes into the
792 | response iterable of the response object.
793 | """
794 |
795 | mode = "wb+"
796 |
797 | def __init__(self, response: Response):
798 | self.response = response
799 | self.closed = False
800 |
801 | def write(self, value: bytes) -> int:
802 | if self.closed:
803 | raise ValueError("I/O operation on closed file")
804 | self.response._ensure_sequence(mutable=True)
805 | self.response.response.append(value) # type: ignore
806 | self.response.headers.pop("Content-Length", None)
807 | return len(value)
808 |
809 | def writelines(self, seq: t.Iterable[bytes]) -> None:
810 | for item in seq:
811 | self.write(item)
812 |
813 | def close(self) -> None:
814 | self.closed = True
815 |
816 | def flush(self) -> None:
817 | if self.closed:
818 | raise ValueError("I/O operation on closed file")
819 |
820 | def isatty(self) -> bool:
821 | if self.closed:
822 | raise ValueError("I/O operation on closed file")
823 | return False
824 |
825 | def tell(self) -> int:
826 | self.response._ensure_sequence()
827 | return sum(map(len, self.response.response))
828 |
829 | @property
830 | def encoding(self) -> str:
831 | return "utf-8"
832 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/werkzeug/routing/rules.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import ast
4 | import re
5 | import typing as t
6 | from dataclasses import dataclass
7 | from string import Template
8 | from types import CodeType
9 | from urllib.parse import quote
10 |
11 | from ..datastructures import iter_multi_items
12 | from ..urls import _urlencode
13 | from .converters import ValidationError
14 |
15 | if t.TYPE_CHECKING:
16 | from .converters import BaseConverter
17 | from .map import Map
18 |
19 |
20 | class Weighting(t.NamedTuple):
21 | number_static_weights: int
22 | static_weights: list[tuple[int, int]]
23 | number_argument_weights: int
24 | argument_weights: list[int]
25 |
26 |
27 | @dataclass
28 | class RulePart:
29 | """A part of a rule.
30 |
31 | Rules can be represented by parts as delimited by `/` with
32 | instances of this class representing those parts. The *content* is
33 | either the raw content if *static* or a regex string to match
34 | against. The *weight* can be used to order parts when matching.
35 |
36 | """
37 |
38 | content: str
39 | final: bool
40 | static: bool
41 | suffixed: bool
42 | weight: Weighting
43 |
44 |
45 | _part_re = re.compile(
46 | r"""
47 | (?:
48 | (?P<slash>/) # a slash
49 | |
50 | (?P<static>[^</]+) # static rule data
51 | |
52 | (?:
53 | <
54 | (?:
55 | (?P<converter>[a-zA-Z_][a-zA-Z0-9_]*) # converter name
56 | (?:\((?P<arguments>.*?)\))? # converter arguments
57 | : # variable delimiter
58 | )?
59 | (?P<variable>[a-zA-Z_][a-zA-Z0-9_]*) # variable name
60 | >
61 | )
62 | )
63 | """,
64 | re.VERBOSE,
65 | )
66 |
67 | _simple_rule_re = re.compile(r"<([^>]+)>")
68 | _converter_args_re = re.compile(
69 | r"""
70 | \s*
71 | ((?P<name>\w+)\s*=\s*)?
72 | (?P<value>
73 | True|False|
74 | \d+.\d+|
75 | \d+.|
76 | \d+|
77 | [\w\d_.]+|
78 | [urUR]?(?P<stringval>"[^"]*?"|'[^']*')
79 | )\s*,
80 | """,
81 | re.VERBOSE,
82 | )
83 |
84 |
85 | _PYTHON_CONSTANTS = {"None": None, "True": True, "False": False}
86 |
87 |
88 | def _find(value: str, target: str, pos: int) -> int:
89 | """Find the *target* in *value* after *pos*.
90 |
91 | Returns the *value* length if *target* isn't found.
92 | """
93 | try:
94 | return value.index(target, pos)
95 | except ValueError:
96 | return len(value)
97 |
98 |
99 | def _pythonize(value: str) -> None | bool | int | float | str:
100 | if value in _PYTHON_CONSTANTS:
101 | return _PYTHON_CONSTANTS[value]
102 | for convert in int, float:
103 | try:
104 | return convert(value)
105 | except ValueError:
106 | pass
107 | if value[:1] == value[-1:] and value[0] in "\"'":
108 | value = value[1:-1]
109 | return str(value)
110 |
111 |
112 | def parse_converter_args(argstr: str) -> tuple[tuple[t.Any, ...], dict[str, t.Any]]:
113 | argstr += ","
114 | args = []
115 | kwargs = {}
116 | position = 0
117 |
118 | for item in _converter_args_re.finditer(argstr):
119 | if item.start() != position:
120 | raise ValueError(
121 | f"Cannot parse converter argument '{argstr[position:item.start()]}'"
122 | )
123 |
124 | value = item.group("stringval")
125 | if value is None:
126 | value = item.group("value")
127 | value = _pythonize(value)
128 | if not item.group("name"):
129 | args.append(value)
130 | else:
131 | name = item.group("name")
132 | kwargs[name] = value
133 | position = item.end()
134 |
135 | return tuple(args), kwargs
136 |
137 |
138 | class RuleFactory:
139 | """As soon as you have more complex URL setups it's a good idea to use rule
140 | factories to avoid repetitive tasks. Some of them are builtin, others can
141 | be added by subclassing `RuleFactory` and overriding `get_rules`.
142 | """
143 |
144 | def get_rules(self, map: Map) -> t.Iterable[Rule]:
145 | """Subclasses of `RuleFactory` have to override this method and return
146 | an iterable of rules."""
147 | raise NotImplementedError()
148 |
149 |
150 | class Subdomain(RuleFactory):
151 | """All URLs provided by this factory have the subdomain set to a
152 | specific domain. For example if you want to use the subdomain for
153 | the current language this can be a good setup::
154 |
155 | url_map = Map([
156 | Rule('/', endpoint='#select_language'),
157 | Subdomain('<string(length=2):lang_code>', [
158 | Rule('/', endpoint='index'),
159 | Rule('/about', endpoint='about'),
160 | Rule('/help', endpoint='help')
161 | ])
162 | ])
163 |
164 | All the rules except for the ``'#select_language'`` endpoint will now
165 | listen on a two letter long subdomain that holds the language code
166 | for the current request.
167 | """
168 |
169 | def __init__(self, subdomain: str, rules: t.Iterable[RuleFactory]) -> None:
170 | self.subdomain = subdomain
171 | self.rules = rules
172 |
173 | def get_rules(self, map: Map) -> t.Iterator[Rule]:
174 | for rulefactory in self.rules:
175 | for rule in rulefactory.get_rules(map):
176 | rule = rule.empty()
177 | rule.subdomain = self.subdomain
178 | yield rule
179 |
180 |
181 | class Submount(RuleFactory):
182 | """Like `Subdomain` but prefixes the URL rule with a given string::
183 |
184 | url_map = Map([
185 | Rule('/', endpoint='index'),
186 | Submount('/blog', [
187 | Rule('/', endpoint='blog/index'),
188 | Rule('/entry/<entry_slug>', endpoint='blog/show')
189 | ])
190 | ])
191 |
192 | Now the rule ``'blog/show'`` matches ``/blog/entry/<entry_slug>``.
193 | """
194 |
195 | def __init__(self, path: str, rules: t.Iterable[RuleFactory]) -> None:
196 | self.path = path.rstrip("/")
197 | self.rules = rules
198 |
199 | def get_rules(self, map: Map) -> t.Iterator[Rule]:
200 | for rulefactory in self.rules:
201 | for rule in rulefactory.get_rules(map):
202 | rule = rule.empty()
203 | rule.rule = self.path + rule.rule
204 | yield rule
205 |
206 |
207 | class EndpointPrefix(RuleFactory):
208 | """Prefixes all endpoints (which must be strings for this factory) with
209 | another string. This can be useful for sub applications::
210 |
211 | url_map = Map([
212 | Rule('/', endpoint='index'),
213 | EndpointPrefix('blog/', [Submount('/blog', [
214 | Rule('/', endpoint='index'),
215 | Rule('/entry/<entry_slug>', endpoint='show')
216 | ])])
217 | ])
218 | """
219 |
220 | def __init__(self, prefix: str, rules: t.Iterable[RuleFactory]) -> None:
221 | self.prefix = prefix
222 | self.rules = rules
223 |
224 | def get_rules(self, map: Map) -> t.Iterator[Rule]:
225 | for rulefactory in self.rules:
226 | for rule in rulefactory.get_rules(map):
227 | rule = rule.empty()
228 | rule.endpoint = self.prefix + rule.endpoint
229 | yield rule
230 |
231 |
232 | class RuleTemplate:
233 | """Returns copies of the rules wrapped and expands string templates in
234 | the endpoint, rule, defaults or subdomain sections.
235 |
236 | Here a small example for such a rule template::
237 |
238 | from werkzeug.routing import Map, Rule, RuleTemplate
239 |
240 | resource = RuleTemplate([
241 | Rule('/$name/', endpoint='$name.list'),
242 | Rule('/$name/<int:id>', endpoint='$name.show')
243 | ])
244 |
245 | url_map = Map([resource(name='user'), resource(name='page')])
246 |
247 | When a rule template is called the keyword arguments are used to
248 | replace the placeholders in all the string parameters.
249 | """
250 |
251 | def __init__(self, rules: t.Iterable[Rule]) -> None:
252 | self.rules = list(rules)
253 |
254 | def __call__(self, *args: t.Any, **kwargs: t.Any) -> RuleTemplateFactory:
255 | return RuleTemplateFactory(self.rules, dict(*args, **kwargs))
256 |
257 |
258 | class RuleTemplateFactory(RuleFactory):
259 | """A factory that fills in template variables into rules. Used by
260 | `RuleTemplate` internally.
261 |
262 | :internal:
263 | """
264 |
265 | def __init__(
266 | self, rules: t.Iterable[RuleFactory], context: dict[str, t.Any]
267 | ) -> None:
268 | self.rules = rules
269 | self.context = context
270 |
271 | def get_rules(self, map: Map) -> t.Iterator[Rule]:
272 | for rulefactory in self.rules:
273 | for rule in rulefactory.get_rules(map):
274 | new_defaults = subdomain = None
275 | if rule.defaults:
276 | new_defaults = {}
277 | for key, value in rule.defaults.items():
278 | if isinstance(value, str):
279 | value = Template(value).substitute(self.context)
280 | new_defaults[key] = value
281 | if rule.subdomain is not None:
282 | subdomain = Template(rule.subdomain).substitute(self.context)
283 | new_endpoint = rule.endpoint
284 | if isinstance(new_endpoint, str):
285 | new_endpoint = Template(new_endpoint).substitute(self.context)
286 | yield Rule(
287 | Template(rule.rule).substitute(self.context),
288 | new_defaults,
289 | subdomain,
290 | rule.methods,
291 | rule.build_only,
292 | new_endpoint,
293 | rule.strict_slashes,
294 | )
295 |
296 |
297 | _ASTT = t.TypeVar("_ASTT", bound=ast.AST)
298 |
299 |
300 | def _prefix_names(src: str, expected_type: type[_ASTT]) -> _ASTT:
301 | """ast parse and prefix names with `.` to avoid collision with user vars"""
302 | tree: ast.AST = ast.parse(src).body[0]
303 | if isinstance(tree, ast.Expr):
304 | tree = tree.value
305 | if not isinstance(tree, expected_type):
306 | raise TypeError(
307 | f"AST node is of type {type(tree).__name__}, not {expected_type.__name__}"
308 | )
309 | for node in ast.walk(tree):
310 | if isinstance(node, ast.Name):
311 | node.id = f".{node.id}"
312 | return tree
313 |
314 |
315 | _CALL_CONVERTER_CODE_FMT = "self._converters[{elem!r}].to_url()"
316 | _IF_KWARGS_URL_ENCODE_CODE = """\
317 | if kwargs:
318 | params = self._encode_query_vars(kwargs)
319 | q = "?" if params else ""
320 | else:
321 | q = params = ""
322 | """
323 | _IF_KWARGS_URL_ENCODE_AST = _prefix_names(_IF_KWARGS_URL_ENCODE_CODE, ast.If)
324 | _URL_ENCODE_AST_NAMES = (
325 | _prefix_names("q", ast.Name),
326 | _prefix_names("params", ast.Name),
327 | )
328 |
329 |
330 | class Rule(RuleFactory):
331 | """A Rule represents one URL pattern. There are some options for `Rule`
332 | that change the way it behaves and are passed to the `Rule` constructor.
333 | Note that besides the rule-string all arguments *must* be keyword arguments
334 | in order to not break the application on Werkzeug upgrades.
335 |
336 | `string`
337 | Rule strings basically are just normal URL paths with placeholders in
338 | the format ``<converter(arguments):name>`` where the converter and the
339 | arguments are optional. If no converter is defined the `default`
340 | converter is used which means `string` in the normal configuration.
341 |
342 | URL rules that end with a slash are branch URLs, others are leaves.
343 | If you have `strict_slashes` enabled (which is the default), all
344 | branch URLs that are matched without a trailing slash will trigger a
345 | redirect to the same URL with the missing slash appended.
346 |
347 | The converters are defined on the `Map`.
348 |
349 | `endpoint`
350 | The endpoint for this rule. This can be anything. A reference to a
351 | function, a string, a number etc. The preferred way is using a string
352 | because the endpoint is used for URL generation.
353 |
354 | `defaults`
355 | An optional dict with defaults for other rules with the same endpoint.
356 | This is a bit tricky but useful if you want to have unique URLs::
357 |
358 | url_map = Map([
359 | Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
360 | Rule('/all/page/<int:page>', endpoint='all_entries')
361 | ])
362 |
363 | If a user now visits ``http://example.com/all/page/1`` they will be
364 | redirected to ``http://example.com/all/``. If `redirect_defaults` is
365 | disabled on the `Map` instance this will only affect the URL
366 | generation.
367 |
368 | `subdomain`
369 | The subdomain rule string for this rule. If not specified the rule
370 | only matches for the `default_subdomain` of the map. If the map is
371 | not bound to a subdomain this feature is disabled.
372 |
373 | Can be useful if you want to have user profiles on different subdomains
374 | and all subdomains are forwarded to your application::
375 |
376 | url_map = Map([
377 | Rule('/', subdomain='<username>', endpoint='user/homepage'),
378 | Rule('/stats', subdomain='<username>', endpoint='user/stats')
379 | ])
380 |
381 | `methods`
382 | A sequence of http methods this rule applies to. If not specified, all
383 | methods are allowed. For example this can be useful if you want different
384 | endpoints for `POST` and `GET`. If methods are defined and the path
385 | matches but the method matched against is not in this list or in the
386 | list of another rule for that path the error raised is of the type
387 | `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the
388 | list of methods and `HEAD` is not, `HEAD` is added automatically.
389 |
390 | `strict_slashes`
391 | Override the `Map` setting for `strict_slashes` only for this rule. If
392 | not specified the `Map` setting is used.
393 |
394 | `merge_slashes`
395 | Override :attr:`Map.merge_slashes` for this rule.
396 |
397 | `build_only`
398 | Set this to True and the rule will never match but will create a URL
399 | that can be build. This is useful if you have resources on a subdomain
400 | or folder that are not handled by the WSGI application (like static data)
401 |
402 | `redirect_to`
403 | If given this must be either a string or callable. In case of a
404 | callable it's called with the url adapter that triggered the match and
405 | the values of the URL as keyword arguments and has to return the target
406 | for the redirect, otherwise it has to be a string with placeholders in
407 | rule syntax::
408 |
409 | def foo_with_slug(adapter, id):
410 | # ask the database for the slug for the old id. this of
411 | # course has nothing to do with werkzeug.
412 | return f'foo/{Foo.get_slug_for_id(id)}'
413 |
414 | url_map = Map([
415 | Rule('/foo/<slug>', endpoint='foo'),
416 | Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'),
417 | Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug)
418 | ])
419 |
420 | When the rule is matched the routing system will raise a
421 | `RequestRedirect` exception with the target for the redirect.
422 |
423 | Keep in mind that the URL will be joined against the URL root of the
424 | script so don't use a leading slash on the target URL unless you
425 | really mean root of that domain.
426 |
427 | `alias`
428 | If enabled this rule serves as an alias for another rule with the same
429 | endpoint and arguments.
430 |
431 | `host`
432 | If provided and the URL map has host matching enabled this can be
433 | used to provide a match rule for the whole host. This also means
434 | that the subdomain feature is disabled.
435 |
436 | `websocket`
437 | If ``True``, this rule is only matches for WebSocket (``ws://``,
438 | ``wss://``) requests. By default, rules will only match for HTTP
439 | requests.
440 |
441 | .. versionchanged:: 2.1
442 | Percent-encoded newlines (``%0a``), which are decoded by WSGI
443 | servers, are considered when routing instead of terminating the
444 | match early.
445 |
446 | .. versionadded:: 1.0
447 | Added ``websocket``.
448 |
449 | .. versionadded:: 1.0
450 | Added ``merge_slashes``.
451 |
452 | .. versionadded:: 0.7
453 | Added ``alias`` and ``host``.
454 |
455 | .. versionchanged:: 0.6.1
456 | ``HEAD`` is added to ``methods`` if ``GET`` is present.
457 | """
458 |
459 | def __init__(
460 | self,
461 | string: str,
462 | defaults: t.Mapping[str, t.Any] | None = None,
463 | subdomain: str | None = None,
464 | methods: t.Iterable[str] | None = None,
465 | build_only: bool = False,
466 | endpoint: t.Any | None = None,
467 | strict_slashes: bool | None = None,
468 | merge_slashes: bool | None = None,
469 | redirect_to: str | t.Callable[..., str] | None = None,
470 | alias: bool = False,
471 | host: str | None = None,
472 | websocket: bool = False,
473 | ) -> None:
474 | if not string.startswith("/"):
475 | raise ValueError(f"URL rule '{string}' must start with a slash.")
476 |
477 | self.rule = string
478 | self.is_leaf = not string.endswith("/")
479 | self.is_branch = string.endswith("/")
480 |
481 | self.map: Map = None # type: ignore
482 | self.strict_slashes = strict_slashes
483 | self.merge_slashes = merge_slashes
484 | self.subdomain = subdomain
485 | self.host = host
486 | self.defaults = defaults
487 | self.build_only = build_only
488 | self.alias = alias
489 | self.websocket = websocket
490 |
491 | if methods is not None:
492 | if isinstance(methods, str):
493 | raise TypeError("'methods' should be a list of strings.")
494 |
495 | methods = {x.upper() for x in methods}
496 |
497 | if "HEAD" not in methods and "GET" in methods:
498 | methods.add("HEAD")
499 |
500 | if websocket and methods - {"GET", "HEAD", "OPTIONS"}:
501 | raise ValueError(
502 | "WebSocket rules can only use 'GET', 'HEAD', and 'OPTIONS' methods."
503 | )
504 |
505 | self.methods = methods
506 | self.endpoint: t.Any = endpoint
507 | self.redirect_to = redirect_to
508 |
509 | if defaults:
510 | self.arguments = set(map(str, defaults))
511 | else:
512 | self.arguments = set()
513 |
514 | self._converters: dict[str, BaseConverter] = {}
515 | self._trace: list[tuple[bool, str]] = []
516 | self._parts: list[RulePart] = []
517 |
518 | def empty(self) -> Rule:
519 | """
520 | Return an unbound copy of this rule.
521 |
522 | This can be useful if want to reuse an already bound URL for another
523 | map. See ``get_empty_kwargs`` to override what keyword arguments are
524 | provided to the new copy.
525 | """
526 | return type(self)(self.rule, **self.get_empty_kwargs())
527 |
528 | def get_empty_kwargs(self) -> t.Mapping[str, t.Any]:
529 | """
530 | Provides kwargs for instantiating empty copy with empty()
531 |
532 | Use this method to provide custom keyword arguments to the subclass of
533 | ``Rule`` when calling ``some_rule.empty()``. Helpful when the subclass
534 | has custom keyword arguments that are needed at instantiation.
535 |
536 | Must return a ``dict`` that will be provided as kwargs to the new
537 | instance of ``Rule``, following the initial ``self.rule`` value which
538 | is always provided as the first, required positional argument.
539 | """
540 | defaults = None
541 | if self.defaults:
542 | defaults = dict(self.defaults)
543 | return dict(
544 | defaults=defaults,
545 | subdomain=self.subdomain,
546 | methods=self.methods,
547 | build_only=self.build_only,
548 | endpoint=self.endpoint,
549 | strict_slashes=self.strict_slashes,
550 | redirect_to=self.redirect_to,
551 | alias=self.alias,
552 | host=self.host,
553 | )
554 |
555 | def get_rules(self, map: Map) -> t.Iterator[Rule]:
556 | yield self
557 |
558 | def refresh(self) -> None:
559 | """Rebinds and refreshes the URL. Call this if you modified the
560 | rule in place.
561 |
562 | :internal:
563 | """
564 | self.bind(self.map, rebind=True)
565 |
566 | def bind(self, map: Map, rebind: bool = False) -> None:
567 | """Bind the url to a map and create a regular expression based on
568 | the information from the rule itself and the defaults from the map.
569 |
570 | :internal:
571 | """
572 | if self.map is not None and not rebind:
573 | raise RuntimeError(f"url rule {self!r} already bound to map {self.map!r}")
574 | self.map = map
575 | if self.strict_slashes is None:
576 | self.strict_slashes = map.strict_slashes
577 | if self.merge_slashes is None:
578 | self.merge_slashes = map.merge_slashes
579 | if self.subdomain is None:
580 | self.subdomain = map.default_subdomain
581 | self.compile()
582 |
583 | def get_converter(
584 | self,
585 | variable_name: str,
586 | converter_name: str,
587 | args: tuple[t.Any, ...],
588 | kwargs: t.Mapping[str, t.Any],
589 | ) -> BaseConverter:
590 | """Looks up the converter for the given parameter.
591 |
592 | .. versionadded:: 0.9
593 | """
594 | if converter_name not in self.map.converters:
595 | raise LookupError(f"the converter {converter_name!r} does not exist")
596 | return self.map.converters[converter_name](self.map, *args, **kwargs)
597 |
598 | def _encode_query_vars(self, query_vars: t.Mapping[str, t.Any]) -> str:
599 | items: t.Iterable[tuple[str, str]] = iter_multi_items(query_vars)
600 |
601 | if self.map.sort_parameters:
602 | items = sorted(items, key=self.map.sort_key)
603 |
604 | return _urlencode(items)
605 |
606 | def _parse_rule(self, rule: str) -> t.Iterable[RulePart]:
607 | content = ""
608 | static = True
609 | argument_weights = []
610 | static_weights: list[tuple[int, int]] = []
611 | final = False
612 | convertor_number = 0
613 |
614 | pos = 0
615 | while pos < len(rule):
616 | match = _part_re.match(rule, pos)
617 | if match is None:
618 | raise ValueError(f"malformed url rule: {rule!r}")
619 |
620 | data = match.groupdict()
621 | if data["static"] is not None:
622 | static_weights.append((len(static_weights), -len(data["static"])))
623 | self._trace.append((False, data["static"]))
624 | content += data["static"] if static else re.escape(data["static"])
625 |
626 | if data["variable"] is not None:
627 | if static:
628 | # Switching content to represent regex, hence the need to escape
629 | content = re.escape(content)
630 | static = False
631 | c_args, c_kwargs = parse_converter_args(data["arguments"] or "")
632 | convobj = self.get_converter(
633 | data["variable"], data["converter"] or "default", c_args, c_kwargs
634 | )
635 | self._converters[data["variable"]] = convobj
636 | self.arguments.add(data["variable"])
637 | if not convobj.part_isolating:
638 | final = True
639 | content += f"(?P<__werkzeug_{convertor_number}>{convobj.regex})"
640 | convertor_number += 1
641 | argument_weights.append(convobj.weight)
642 | self._trace.append((True, data["variable"]))
643 |
644 | if data["slash"] is not None:
645 | self._trace.append((False, "/"))
646 | if final:
647 | content += "/"
648 | else:
649 | if not static:
650 | content += r"\Z"
651 | weight = Weighting(
652 | -len(static_weights),
653 | static_weights,
654 | -len(argument_weights),
655 | argument_weights,
656 | )
657 | yield RulePart(
658 | content=content,
659 | final=final,
660 | static=static,
661 | suffixed=False,
662 | weight=weight,
663 | )
664 | content = ""
665 | static = True
666 | argument_weights = []
667 | static_weights = []
668 | final = False
669 | convertor_number = 0
670 |
671 | pos = match.end()
672 |
673 | suffixed = False
674 | if final and content[-1] == "/":
675 | # If a converter is part_isolating=False (matches slashes) and ends with a
676 | # slash, augment the regex to support slash redirects.
677 | suffixed = True
678 | content = content[:-1] + "(?<!/)(/?)"
679 | if not static:
680 | content += r"\Z"
681 | weight = Weighting(
682 | -len(static_weights),
683 | static_weights,
684 | -len(argument_weights),
685 | argument_weights,
686 | )
687 | yield RulePart(
688 | content=content,
689 | final=final,
690 | static=static,
691 | suffixed=suffixed,
692 | weight=weight,
693 | )
694 | if suffixed:
695 | yield RulePart(
696 | content="", final=False, static=True, suffixed=False, weight=weight
697 | )
698 |
699 | def compile(self) -> None:
700 | """Compiles the regular expression and stores it."""
701 | assert self.map is not None, "rule not bound"
702 |
703 | if self.map.host_matching:
704 | domain_rule = self.host or ""
705 | else:
706 | domain_rule = self.subdomain or ""
707 | self._parts = []
708 | self._trace = []
709 | self._converters = {}
710 | if domain_rule == "":
711 | self._parts = [
712 | RulePart(
713 | content="",
714 | final=False,
715 | static=True,
716 | suffixed=False,
717 | weight=Weighting(0, [], 0, []),
718 | )
719 | ]
720 | else:
721 | self._parts.extend(self._parse_rule(domain_rule))
722 | self._trace.append((False, "|"))
723 | rule = self.rule
724 | if self.merge_slashes:
725 | rule = re.sub("/{2,}?", "/", self.rule)
726 | self._parts.extend(self._parse_rule(rule))
727 |
728 | self._build: t.Callable[..., tuple[str, str]]
729 | self._build = self._compile_builder(False).__get__(self, None)
730 | self._build_unknown: t.Callable[..., tuple[str, str]]
731 | self._build_unknown = self._compile_builder(True).__get__(self, None)
732 |
733 | @staticmethod
734 | def _get_func_code(code: CodeType, name: str) -> t.Callable[..., tuple[str, str]]:
735 | globs: dict[str, t.Any] = {}
736 | locs: dict[str, t.Any] = {}
737 | exec(code, globs, locs)
738 | return locs[name] # type: ignore
739 |
740 | def _compile_builder(
741 | self, append_unknown: bool = True
742 | ) -> t.Callable[..., tuple[str, str]]:
743 | defaults = self.defaults or {}
744 | dom_ops: list[tuple[bool, str]] = []
745 | url_ops: list[tuple[bool, str]] = []
746 |
747 | opl = dom_ops
748 | for is_dynamic, data in self._trace:
749 | if data == "|" and opl is dom_ops:
750 | opl = url_ops
751 | continue
752 | # this seems like a silly case to ever come up but:
753 | # if a default is given for a value that appears in the rule,
754 | # resolve it to a constant ahead of time
755 | if is_dynamic and data in defaults:
756 | data = self._converters[data].to_url(defaults[data])
757 | opl.append((False, data))
758 | elif not is_dynamic:
759 | # safe = https://url.spec.whatwg.org/#url-path-segment-string
760 | opl.append((False, quote(data, safe="!$&'()*+,/:;=@")))
761 | else:
762 | opl.append((True, data))
763 |
764 | def _convert(elem: str) -> ast.Call:
765 | ret = _prefix_names(_CALL_CONVERTER_CODE_FMT.format(elem=elem), ast.Call)
766 | ret.args = [ast.Name(elem, ast.Load())]
767 | return ret
768 |
769 | def _parts(ops: list[tuple[bool, str]]) -> list[ast.expr]:
770 | parts: list[ast.expr] = [
771 | _convert(elem) if is_dynamic else ast.Constant(elem)
772 | for is_dynamic, elem in ops
773 | ]
774 | parts = parts or [ast.Constant("")]
775 | # constant fold
776 | ret = [parts[0]]
777 | for p in parts[1:]:
778 | if isinstance(p, ast.Constant) and isinstance(ret[-1], ast.Constant):
779 | ret[-1] = ast.Constant(ret[-1].value + p.value)
780 | else:
781 | ret.append(p)
782 | return ret
783 |
784 | dom_parts = _parts(dom_ops)
785 | url_parts = _parts(url_ops)
786 | body: list[ast.stmt]
787 | if not append_unknown:
788 | body = []
789 | else:
790 | body = [_IF_KWARGS_URL_ENCODE_AST]
791 | url_parts.extend(_URL_ENCODE_AST_NAMES)
792 |
793 | def _join(parts: list[ast.expr]) -> ast.expr:
794 | if len(parts) == 1: # shortcut
795 | return parts[0]
796 | return ast.JoinedStr(parts)
797 |
798 | body.append(
799 | ast.Return(ast.Tuple([_join(dom_parts), _join(url_parts)], ast.Load()))
800 | )
801 |
802 | pargs = [
803 | elem
804 | for is_dynamic, elem in dom_ops + url_ops
805 | if is_dynamic and elem not in defaults
806 | ]
807 | kargs = [str(k) for k in defaults]
808 |
809 | func_ast = _prefix_names("def _(): pass", ast.FunctionDef)
810 | func_ast.name = f"<builder:{self.rule!r}>"
811 | func_ast.args.args.append(ast.arg(".self", None))
812 | for arg in pargs + kargs:
813 | func_ast.args.args.append(ast.arg(arg, None))
814 | func_ast.args.kwarg = ast.arg(".kwargs", None)
815 | for _ in kargs:
816 | func_ast.args.defaults.append(ast.Constant(""))
817 | func_ast.body = body
818 |
819 | # Use `ast.parse` instead of `ast.Module` for better portability, since the
820 | # signature of `ast.Module` can change.
821 | module = ast.parse("")
822 | module.body = [func_ast]
823 |
824 | # mark everything as on line 1, offset 0
825 | # less error-prone than `ast.fix_missing_locations`
826 | # bad line numbers cause an assert to fail in debug builds
827 | for node in ast.walk(module):
828 | if "lineno" in node._attributes:
829 | node.lineno = 1 # type: ignore[attr-defined]
830 | if "end_lineno" in node._attributes:
831 | node.end_lineno = node.lineno # type: ignore[attr-defined]
832 | if "col_offset" in node._attributes:
833 | node.col_offset = 0 # type: ignore[attr-defined]
834 | if "end_col_offset" in node._attributes:
835 | node.end_col_offset = node.col_offset # type: ignore[attr-defined]
836 |
837 | code = compile(module, "<werkzeug routing>", "exec")
838 | return self._get_func_code(code, func_ast.name)
839 |
840 | def build(
841 | self, values: t.Mapping[str, t.Any], append_unknown: bool = True
842 | ) -> tuple[str, str] | None:
843 | """Assembles the relative url for that rule and the subdomain.
844 | If building doesn't work for some reasons `None` is returned.
845 |
846 | :internal:
847 | """
848 | try:
849 | if append_unknown:
850 | return self._build_unknown(**values)
851 | else:
852 | return self._build(**values)
853 | except ValidationError:
854 | return None
855 |
856 | def provides_defaults_for(self, rule: Rule) -> bool:
857 | """Check if this rule has defaults for a given rule.
858 |
859 | :internal:
860 | """
861 | return bool(
862 | not self.build_only
863 | and self.defaults
864 | and self.endpoint == rule.endpoint
865 | and self != rule
866 | and self.arguments == rule.arguments
867 | )
868 |
869 | def suitable_for(
870 | self, values: t.Mapping[str, t.Any], method: str | None = None
871 | ) -> bool:
872 | """Check if the dict of values has enough data for url generation.
873 |
874 | :internal:
875 | """
876 | # if a method was given explicitly and that method is not supported
877 | # by this rule, this rule is not suitable.
878 | if (
879 | method is not None
880 | and self.methods is not None
881 | and method not in self.methods
882 | ):
883 | return False
884 |
885 | defaults = self.defaults or ()
886 |
887 | # all arguments required must be either in the defaults dict or
888 | # the value dictionary otherwise it's not suitable
889 | for key in self.arguments:
890 | if key not in defaults and key not in values:
891 | return False
892 |
893 | # in case defaults are given we ensure that either the value was
894 | # skipped or the value is the same as the default value.
895 | if defaults:
896 | for key, value in defaults.items():
897 | if key in values and value != values[key]:
898 | return False
899 |
900 | return True
901 |
902 | def build_compare_key(self) -> tuple[int, int, int]:
903 | """The build compare key for sorting.
904 |
905 | :internal:
906 | """
907 | return (1 if self.alias else 0, -len(self.arguments), -len(self.defaults or ()))
908 |
909 | def __eq__(self, other: object) -> bool:
910 | return isinstance(other, type(self)) and self._trace == other._trace
911 |
912 | __hash__ = None # type: ignore
913 |
914 | def __str__(self) -> str:
915 | return self.rule
916 |
917 | def __repr__(self) -> str:
918 | if self.map is None:
919 | return f"<{type(self).__name__} (unbound)>"
920 | parts = []
921 | for is_dynamic, data in self._trace:
922 | if is_dynamic:
923 | parts.append(f"<{data}>")
924 | else:
925 | parts.append(data)
926 | parts_str = "".join(parts).lstrip("|")
927 | methods = f" ({', '.join(self.methods)})" if self.methods is not None else ""
928 | return f"<{type(self).__name__} {parts_str!r}{methods} -> {self.endpoint}>"
929 |
```