This is page 135 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/http.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import email.utils
4 | import re
5 | import typing as t
6 | import warnings
7 | from datetime import date
8 | from datetime import datetime
9 | from datetime import time
10 | from datetime import timedelta
11 | from datetime import timezone
12 | from enum import Enum
13 | from hashlib import sha1
14 | from time import mktime
15 | from time import struct_time
16 | from urllib.parse import quote
17 | from urllib.parse import unquote
18 | from urllib.request import parse_http_list as _parse_list_header
19 |
20 | from ._internal import _dt_as_utc
21 | from ._internal import _plain_int
22 |
23 | if t.TYPE_CHECKING:
24 | from _typeshed.wsgi import WSGIEnvironment
25 |
26 | _token_chars = frozenset(
27 | "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"
28 | )
29 | _etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
30 | _entity_headers = frozenset(
31 | [
32 | "allow",
33 | "content-encoding",
34 | "content-language",
35 | "content-length",
36 | "content-location",
37 | "content-md5",
38 | "content-range",
39 | "content-type",
40 | "expires",
41 | "last-modified",
42 | ]
43 | )
44 | _hop_by_hop_headers = frozenset(
45 | [
46 | "connection",
47 | "keep-alive",
48 | "proxy-authenticate",
49 | "proxy-authorization",
50 | "te",
51 | "trailer",
52 | "transfer-encoding",
53 | "upgrade",
54 | ]
55 | )
56 | HTTP_STATUS_CODES = {
57 | 100: "Continue",
58 | 101: "Switching Protocols",
59 | 102: "Processing",
60 | 103: "Early Hints", # see RFC 8297
61 | 200: "OK",
62 | 201: "Created",
63 | 202: "Accepted",
64 | 203: "Non Authoritative Information",
65 | 204: "No Content",
66 | 205: "Reset Content",
67 | 206: "Partial Content",
68 | 207: "Multi Status",
69 | 208: "Already Reported", # see RFC 5842
70 | 226: "IM Used", # see RFC 3229
71 | 300: "Multiple Choices",
72 | 301: "Moved Permanently",
73 | 302: "Found",
74 | 303: "See Other",
75 | 304: "Not Modified",
76 | 305: "Use Proxy",
77 | 306: "Switch Proxy", # unused
78 | 307: "Temporary Redirect",
79 | 308: "Permanent Redirect",
80 | 400: "Bad Request",
81 | 401: "Unauthorized",
82 | 402: "Payment Required", # unused
83 | 403: "Forbidden",
84 | 404: "Not Found",
85 | 405: "Method Not Allowed",
86 | 406: "Not Acceptable",
87 | 407: "Proxy Authentication Required",
88 | 408: "Request Timeout",
89 | 409: "Conflict",
90 | 410: "Gone",
91 | 411: "Length Required",
92 | 412: "Precondition Failed",
93 | 413: "Request Entity Too Large",
94 | 414: "Request URI Too Long",
95 | 415: "Unsupported Media Type",
96 | 416: "Requested Range Not Satisfiable",
97 | 417: "Expectation Failed",
98 | 418: "I'm a teapot", # see RFC 2324
99 | 421: "Misdirected Request", # see RFC 7540
100 | 422: "Unprocessable Entity",
101 | 423: "Locked",
102 | 424: "Failed Dependency",
103 | 425: "Too Early", # see RFC 8470
104 | 426: "Upgrade Required",
105 | 428: "Precondition Required", # see RFC 6585
106 | 429: "Too Many Requests",
107 | 431: "Request Header Fields Too Large",
108 | 449: "Retry With", # proprietary MS extension
109 | 451: "Unavailable For Legal Reasons",
110 | 500: "Internal Server Error",
111 | 501: "Not Implemented",
112 | 502: "Bad Gateway",
113 | 503: "Service Unavailable",
114 | 504: "Gateway Timeout",
115 | 505: "HTTP Version Not Supported",
116 | 506: "Variant Also Negotiates", # see RFC 2295
117 | 507: "Insufficient Storage",
118 | 508: "Loop Detected", # see RFC 5842
119 | 510: "Not Extended",
120 | 511: "Network Authentication Failed",
121 | }
122 |
123 |
124 | class COEP(Enum):
125 | """Cross Origin Embedder Policies"""
126 |
127 | UNSAFE_NONE = "unsafe-none"
128 | REQUIRE_CORP = "require-corp"
129 |
130 |
131 | class COOP(Enum):
132 | """Cross Origin Opener Policies"""
133 |
134 | UNSAFE_NONE = "unsafe-none"
135 | SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups"
136 | SAME_ORIGIN = "same-origin"
137 |
138 |
139 | def quote_header_value(value: t.Any, allow_token: bool = True) -> str:
140 | """Add double quotes around a header value. If the header contains only ASCII token
141 | characters, it will be returned unchanged. If the header contains ``"`` or ``\\``
142 | characters, they will be escaped with an additional ``\\`` character.
143 |
144 | This is the reverse of :func:`unquote_header_value`.
145 |
146 | :param value: The value to quote. Will be converted to a string.
147 | :param allow_token: Disable to quote the value even if it only has token characters.
148 |
149 | .. versionchanged:: 3.0
150 | Passing bytes is not supported.
151 |
152 | .. versionchanged:: 3.0
153 | The ``extra_chars`` parameter is removed.
154 |
155 | .. versionchanged:: 2.3
156 | The value is quoted if it is the empty string.
157 |
158 | .. versionadded:: 0.5
159 | """
160 | value_str = str(value)
161 |
162 | if not value_str:
163 | return '""'
164 |
165 | if allow_token:
166 | token_chars = _token_chars
167 |
168 | if token_chars.issuperset(value_str):
169 | return value_str
170 |
171 | value_str = value_str.replace("\\", "\\\\").replace('"', '\\"')
172 | return f'"{value_str}"'
173 |
174 |
175 | def unquote_header_value(value: str) -> str:
176 | """Remove double quotes and decode slash-escaped ``"`` and ``\\`` characters in a
177 | header value.
178 |
179 | This is the reverse of :func:`quote_header_value`.
180 |
181 | :param value: The header value to unquote.
182 |
183 | .. versionchanged:: 3.0
184 | The ``is_filename`` parameter is removed.
185 | """
186 | if len(value) >= 2 and value[0] == value[-1] == '"':
187 | value = value[1:-1]
188 | return value.replace("\\\\", "\\").replace('\\"', '"')
189 |
190 | return value
191 |
192 |
193 | def dump_options_header(header: str | None, options: t.Mapping[str, t.Any]) -> str:
194 | """Produce a header value and ``key=value`` parameters separated by semicolons
195 | ``;``. For example, the ``Content-Type`` header.
196 |
197 | .. code-block:: python
198 |
199 | dump_options_header("text/html", {"charset": "UTF-8"})
200 | 'text/html; charset=UTF-8'
201 |
202 | This is the reverse of :func:`parse_options_header`.
203 |
204 | If a value contains non-token characters, it will be quoted.
205 |
206 | If a value is ``None``, the parameter is skipped.
207 |
208 | In some keys for some headers, a UTF-8 value can be encoded using a special
209 | ``key*=UTF-8''value`` form, where ``value`` is percent encoded. This function will
210 | not produce that format automatically, but if a given key ends with an asterisk
211 | ``*``, the value is assumed to have that form and will not be quoted further.
212 |
213 | :param header: The primary header value.
214 | :param options: Parameters to encode as ``key=value`` pairs.
215 |
216 | .. versionchanged:: 2.3
217 | Keys with ``None`` values are skipped rather than treated as a bare key.
218 |
219 | .. versionchanged:: 2.2.3
220 | If a key ends with ``*``, its value will not be quoted.
221 | """
222 | segments = []
223 |
224 | if header is not None:
225 | segments.append(header)
226 |
227 | for key, value in options.items():
228 | if value is None:
229 | continue
230 |
231 | if key[-1] == "*":
232 | segments.append(f"{key}={value}")
233 | else:
234 | segments.append(f"{key}={quote_header_value(value)}")
235 |
236 | return "; ".join(segments)
237 |
238 |
239 | def dump_header(iterable: dict[str, t.Any] | t.Iterable[t.Any]) -> str:
240 | """Produce a header value from a list of items or ``key=value`` pairs, separated by
241 | commas ``,``.
242 |
243 | This is the reverse of :func:`parse_list_header`, :func:`parse_dict_header`, and
244 | :func:`parse_set_header`.
245 |
246 | If a value contains non-token characters, it will be quoted.
247 |
248 | If a value is ``None``, the key is output alone.
249 |
250 | In some keys for some headers, a UTF-8 value can be encoded using a special
251 | ``key*=UTF-8''value`` form, where ``value`` is percent encoded. This function will
252 | not produce that format automatically, but if a given key ends with an asterisk
253 | ``*``, the value is assumed to have that form and will not be quoted further.
254 |
255 | .. code-block:: python
256 |
257 | dump_header(["foo", "bar baz"])
258 | 'foo, "bar baz"'
259 |
260 | dump_header({"foo": "bar baz"})
261 | 'foo="bar baz"'
262 |
263 | :param iterable: The items to create a header from.
264 |
265 | .. versionchanged:: 3.0
266 | The ``allow_token`` parameter is removed.
267 |
268 | .. versionchanged:: 2.2.3
269 | If a key ends with ``*``, its value will not be quoted.
270 | """
271 | if isinstance(iterable, dict):
272 | items = []
273 |
274 | for key, value in iterable.items():
275 | if value is None:
276 | items.append(key)
277 | elif key[-1] == "*":
278 | items.append(f"{key}={value}")
279 | else:
280 | items.append(f"{key}={quote_header_value(value)}")
281 | else:
282 | items = [quote_header_value(x) for x in iterable]
283 |
284 | return ", ".join(items)
285 |
286 |
287 | def dump_csp_header(header: ds.ContentSecurityPolicy) -> str:
288 | """Dump a Content Security Policy header.
289 |
290 | These are structured into policies such as "default-src 'self';
291 | script-src 'self'".
292 |
293 | .. versionadded:: 1.0.0
294 | Support for Content Security Policy headers was added.
295 |
296 | """
297 | return "; ".join(f"{key} {value}" for key, value in header.items())
298 |
299 |
300 | def parse_list_header(value: str) -> list[str]:
301 | """Parse a header value that consists of a list of comma separated items according
302 | to `RFC 9110 <https://httpwg.org/specs/rfc9110.html#abnf.extension>`__.
303 |
304 | This extends :func:`urllib.request.parse_http_list` to remove surrounding quotes
305 | from values.
306 |
307 | .. code-block:: python
308 |
309 | parse_list_header('token, "quoted value"')
310 | ['token', 'quoted value']
311 |
312 | This is the reverse of :func:`dump_header`.
313 |
314 | :param value: The header value to parse.
315 | """
316 | result = []
317 |
318 | for item in _parse_list_header(value):
319 | if len(item) >= 2 and item[0] == item[-1] == '"':
320 | item = item[1:-1]
321 |
322 | result.append(item)
323 |
324 | return result
325 |
326 |
327 | def parse_dict_header(value: str) -> dict[str, str | None]:
328 | """Parse a list header using :func:`parse_list_header`, then parse each item as a
329 | ``key=value`` pair.
330 |
331 | .. code-block:: python
332 |
333 | parse_dict_header('a=b, c="d, e", f')
334 | {"a": "b", "c": "d, e", "f": None}
335 |
336 | This is the reverse of :func:`dump_header`.
337 |
338 | If a key does not have a value, it is ``None``.
339 |
340 | This handles charsets for values as described in
341 | `RFC 2231 <https://www.rfc-editor.org/rfc/rfc2231#section-3>`__. Only ASCII, UTF-8,
342 | and ISO-8859-1 charsets are accepted, otherwise the value remains quoted.
343 |
344 | :param value: The header value to parse.
345 |
346 | .. versionchanged:: 3.0
347 | Passing bytes is not supported.
348 |
349 | .. versionchanged:: 3.0
350 | The ``cls`` argument is removed.
351 |
352 | .. versionchanged:: 2.3
353 | Added support for ``key*=charset''value`` encoded items.
354 |
355 | .. versionchanged:: 0.9
356 | The ``cls`` argument was added.
357 | """
358 | result: dict[str, str | None] = {}
359 |
360 | for item in parse_list_header(value):
361 | key, has_value, value = item.partition("=")
362 | key = key.strip()
363 |
364 | if not has_value:
365 | result[key] = None
366 | continue
367 |
368 | value = value.strip()
369 | encoding: str | None = None
370 |
371 | if key[-1] == "*":
372 | # key*=charset''value becomes key=value, where value is percent encoded
373 | # adapted from parse_options_header, without the continuation handling
374 | key = key[:-1]
375 | match = _charset_value_re.match(value)
376 |
377 | if match:
378 | # If there is a charset marker in the value, split it off.
379 | encoding, value = match.groups()
380 | encoding = encoding.lower()
381 |
382 | # A safe list of encodings. Modern clients should only send ASCII or UTF-8.
383 | # This list will not be extended further. An invalid encoding will leave the
384 | # value quoted.
385 | if encoding in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}:
386 | # invalid bytes are replaced during unquoting
387 | value = unquote(value, encoding=encoding)
388 |
389 | if len(value) >= 2 and value[0] == value[-1] == '"':
390 | value = value[1:-1]
391 |
392 | result[key] = value
393 |
394 | return result
395 |
396 |
397 | # https://httpwg.org/specs/rfc9110.html#parameter
398 | _parameter_key_re = re.compile(r"([\w!#$%&'*+\-.^`|~]+)=", flags=re.ASCII)
399 | _parameter_token_value_re = re.compile(r"[\w!#$%&'*+\-.^`|~]+", flags=re.ASCII)
400 | # https://www.rfc-editor.org/rfc/rfc2231#section-4
401 | _charset_value_re = re.compile(
402 | r"""
403 | ([\w!#$%&*+\-.^`|~]*)' # charset part, could be empty
404 | [\w!#$%&*+\-.^`|~]*' # don't care about language part, usually empty
405 | ([\w!#$%&'*+\-.^`|~]+) # one or more token chars with percent encoding
406 | """,
407 | re.ASCII | re.VERBOSE,
408 | )
409 | # https://www.rfc-editor.org/rfc/rfc2231#section-3
410 | _continuation_re = re.compile(r"\*(\d+)$", re.ASCII)
411 |
412 |
413 | def parse_options_header(value: str | None) -> tuple[str, dict[str, str]]:
414 | """Parse a header that consists of a value with ``key=value`` parameters separated
415 | by semicolons ``;``. For example, the ``Content-Type`` header.
416 |
417 | .. code-block:: python
418 |
419 | parse_options_header("text/html; charset=UTF-8")
420 | ('text/html', {'charset': 'UTF-8'})
421 |
422 | parse_options_header("")
423 | ("", {})
424 |
425 | This is the reverse of :func:`dump_options_header`.
426 |
427 | This parses valid parameter parts as described in
428 | `RFC 9110 <https://httpwg.org/specs/rfc9110.html#parameter>`__. Invalid parts are
429 | skipped.
430 |
431 | This handles continuations and charsets as described in
432 | `RFC 2231 <https://www.rfc-editor.org/rfc/rfc2231#section-3>`__, although not as
433 | strictly as the RFC. Only ASCII, UTF-8, and ISO-8859-1 charsets are accepted,
434 | otherwise the value remains quoted.
435 |
436 | Clients may not be consistent in how they handle a quote character within a quoted
437 | value. The `HTML Standard <https://html.spec.whatwg.org/#multipart-form-data>`__
438 | replaces it with ``%22`` in multipart form data.
439 | `RFC 9110 <https://httpwg.org/specs/rfc9110.html#quoted.strings>`__ uses backslash
440 | escapes in HTTP headers. Both are decoded to the ``"`` character.
441 |
442 | Clients may not be consistent in how they handle non-ASCII characters. HTML
443 | documents must declare ``<meta charset=UTF-8>``, otherwise browsers may replace with
444 | HTML character references, which can be decoded using :func:`html.unescape`.
445 |
446 | :param value: The header value to parse.
447 | :return: ``(value, options)``, where ``options`` is a dict
448 |
449 | .. versionchanged:: 2.3
450 | Invalid parts, such as keys with no value, quoted keys, and incorrectly quoted
451 | values, are discarded instead of treating as ``None``.
452 |
453 | .. versionchanged:: 2.3
454 | Only ASCII, UTF-8, and ISO-8859-1 are accepted for charset values.
455 |
456 | .. versionchanged:: 2.3
457 | Escaped quotes in quoted values, like ``%22`` and ``\\"``, are handled.
458 |
459 | .. versionchanged:: 2.2
460 | Option names are always converted to lowercase.
461 |
462 | .. versionchanged:: 2.2
463 | The ``multiple`` parameter was removed.
464 |
465 | .. versionchanged:: 0.15
466 | :rfc:`2231` parameter continuations are handled.
467 |
468 | .. versionadded:: 0.5
469 | """
470 | if value is None:
471 | return "", {}
472 |
473 | value, _, rest = value.partition(";")
474 | value = value.strip()
475 | rest = rest.strip()
476 |
477 | if not value or not rest:
478 | # empty (invalid) value, or value without options
479 | return value, {}
480 |
481 | # Collect all valid key=value parts without processing the value.
482 | parts: list[tuple[str, str]] = []
483 |
484 | while True:
485 | if (m := _parameter_key_re.match(rest)) is not None:
486 | pk = m.group(1).lower()
487 | rest = rest[m.end() :]
488 |
489 | # Value may be a token.
490 | if (m := _parameter_token_value_re.match(rest)) is not None:
491 | parts.append((pk, m.group()))
492 |
493 | # Value may be a quoted string, find the closing quote.
494 | elif rest[:1] == '"':
495 | pos = 1
496 | length = len(rest)
497 |
498 | while pos < length:
499 | if rest[pos : pos + 2] in {"\\\\", '\\"'}:
500 | # Consume escaped slashes and quotes.
501 | pos += 2
502 | elif rest[pos] == '"':
503 | # Stop at an unescaped quote.
504 | parts.append((pk, rest[: pos + 1]))
505 | rest = rest[pos + 1 :]
506 | break
507 | else:
508 | # Consume any other character.
509 | pos += 1
510 |
511 | # Find the next section delimited by `;`, if any.
512 | if (end := rest.find(";")) == -1:
513 | break
514 |
515 | rest = rest[end + 1 :].lstrip()
516 |
517 | options: dict[str, str] = {}
518 | encoding: str | None = None
519 | continued_encoding: str | None = None
520 |
521 | # For each collected part, process optional charset and continuation,
522 | # unquote quoted values.
523 | for pk, pv in parts:
524 | if pk[-1] == "*":
525 | # key*=charset''value becomes key=value, where value is percent encoded
526 | pk = pk[:-1]
527 | match = _charset_value_re.match(pv)
528 |
529 | if match:
530 | # If there is a valid charset marker in the value, split it off.
531 | encoding, pv = match.groups()
532 | # This might be the empty string, handled next.
533 | encoding = encoding.lower()
534 |
535 | # No charset marker, or marker with empty charset value.
536 | if not encoding:
537 | encoding = continued_encoding
538 |
539 | # A safe list of encodings. Modern clients should only send ASCII or UTF-8.
540 | # This list will not be extended further. An invalid encoding will leave the
541 | # value quoted.
542 | if encoding in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}:
543 | # Continuation parts don't require their own charset marker. This is
544 | # looser than the RFC, it will persist across different keys and allows
545 | # changing the charset during a continuation. But this implementation is
546 | # much simpler than tracking the full state.
547 | continued_encoding = encoding
548 | # invalid bytes are replaced during unquoting
549 | pv = unquote(pv, encoding=encoding)
550 |
551 | # Remove quotes. At this point the value cannot be empty or a single quote.
552 | if pv[0] == pv[-1] == '"':
553 | # HTTP headers use slash, multipart form data uses percent
554 | pv = pv[1:-1].replace("\\\\", "\\").replace('\\"', '"').replace("%22", '"')
555 |
556 | match = _continuation_re.search(pk)
557 |
558 | if match:
559 | # key*0=a; key*1=b becomes key=ab
560 | pk = pk[: match.start()]
561 | options[pk] = options.get(pk, "") + pv
562 | else:
563 | options[pk] = pv
564 |
565 | return value, options
566 |
567 |
568 | _q_value_re = re.compile(r"-?\d+(\.\d+)?", re.ASCII)
569 | _TAnyAccept = t.TypeVar("_TAnyAccept", bound="ds.Accept")
570 |
571 |
572 | @t.overload
573 | def parse_accept_header(value: str | None) -> ds.Accept: ...
574 |
575 |
576 | @t.overload
577 | def parse_accept_header(value: str | None, cls: type[_TAnyAccept]) -> _TAnyAccept: ...
578 |
579 |
580 | def parse_accept_header(
581 | value: str | None, cls: type[_TAnyAccept] | None = None
582 | ) -> _TAnyAccept:
583 | """Parse an ``Accept`` header according to
584 | `RFC 9110 <https://httpwg.org/specs/rfc9110.html#field.accept>`__.
585 |
586 | Returns an :class:`.Accept` instance, which can sort and inspect items based on
587 | their quality parameter. When parsing ``Accept-Charset``, ``Accept-Encoding``, or
588 | ``Accept-Language``, pass the appropriate :class:`.Accept` subclass.
589 |
590 | :param value: The header value to parse.
591 | :param cls: The :class:`.Accept` class to wrap the result in.
592 | :return: An instance of ``cls``.
593 |
594 | .. versionchanged:: 2.3
595 | Parse according to RFC 9110. Items with invalid ``q`` values are skipped.
596 | """
597 | if cls is None:
598 | cls = t.cast(t.Type[_TAnyAccept], ds.Accept)
599 |
600 | if not value:
601 | return cls(None)
602 |
603 | result = []
604 |
605 | for item in parse_list_header(value):
606 | item, options = parse_options_header(item)
607 |
608 | if "q" in options:
609 | # pop q, remaining options are reconstructed
610 | q_str = options.pop("q").strip()
611 |
612 | if _q_value_re.fullmatch(q_str) is None:
613 | # ignore an invalid q
614 | continue
615 |
616 | q = float(q_str)
617 |
618 | if q < 0 or q > 1:
619 | # ignore an invalid q
620 | continue
621 | else:
622 | q = 1
623 |
624 | if options:
625 | # reconstruct the media type with any options
626 | item = dump_options_header(item, options)
627 |
628 | result.append((item, q))
629 |
630 | return cls(result)
631 |
632 |
633 | _TAnyCC = t.TypeVar("_TAnyCC", bound="ds.cache_control._CacheControl")
634 |
635 |
636 | @t.overload
637 | def parse_cache_control_header(
638 | value: str | None,
639 | on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None,
640 | ) -> ds.RequestCacheControl: ...
641 |
642 |
643 | @t.overload
644 | def parse_cache_control_header(
645 | value: str | None,
646 | on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None,
647 | cls: type[_TAnyCC] = ...,
648 | ) -> _TAnyCC: ...
649 |
650 |
651 | def parse_cache_control_header(
652 | value: str | None,
653 | on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None,
654 | cls: type[_TAnyCC] | None = None,
655 | ) -> _TAnyCC:
656 | """Parse a cache control header. The RFC differs between response and
657 | request cache control, this method does not. It's your responsibility
658 | to not use the wrong control statements.
659 |
660 | .. versionadded:: 0.5
661 | The `cls` was added. If not specified an immutable
662 | :class:`~werkzeug.datastructures.RequestCacheControl` is returned.
663 |
664 | :param value: a cache control header to be parsed.
665 | :param on_update: an optional callable that is called every time a value
666 | on the :class:`~werkzeug.datastructures.CacheControl`
667 | object is changed.
668 | :param cls: the class for the returned object. By default
669 | :class:`~werkzeug.datastructures.RequestCacheControl` is used.
670 | :return: a `cls` object.
671 | """
672 | if cls is None:
673 | cls = t.cast("type[_TAnyCC]", ds.RequestCacheControl)
674 |
675 | if not value:
676 | return cls((), on_update)
677 |
678 | return cls(parse_dict_header(value), on_update)
679 |
680 |
681 | _TAnyCSP = t.TypeVar("_TAnyCSP", bound="ds.ContentSecurityPolicy")
682 |
683 |
684 | @t.overload
685 | def parse_csp_header(
686 | value: str | None,
687 | on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None,
688 | ) -> ds.ContentSecurityPolicy: ...
689 |
690 |
691 | @t.overload
692 | def parse_csp_header(
693 | value: str | None,
694 | on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None,
695 | cls: type[_TAnyCSP] = ...,
696 | ) -> _TAnyCSP: ...
697 |
698 |
699 | def parse_csp_header(
700 | value: str | None,
701 | on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None,
702 | cls: type[_TAnyCSP] | None = None,
703 | ) -> _TAnyCSP:
704 | """Parse a Content Security Policy header.
705 |
706 | .. versionadded:: 1.0.0
707 | Support for Content Security Policy headers was added.
708 |
709 | :param value: a csp header to be parsed.
710 | :param on_update: an optional callable that is called every time a value
711 | on the object is changed.
712 | :param cls: the class for the returned object. By default
713 | :class:`~werkzeug.datastructures.ContentSecurityPolicy` is used.
714 | :return: a `cls` object.
715 | """
716 | if cls is None:
717 | cls = t.cast("type[_TAnyCSP]", ds.ContentSecurityPolicy)
718 |
719 | if value is None:
720 | return cls((), on_update)
721 |
722 | items = []
723 |
724 | for policy in value.split(";"):
725 | policy = policy.strip()
726 |
727 | # Ignore badly formatted policies (no space)
728 | if " " in policy:
729 | directive, value = policy.strip().split(" ", 1)
730 | items.append((directive.strip(), value.strip()))
731 |
732 | return cls(items, on_update)
733 |
734 |
735 | def parse_set_header(
736 | value: str | None,
737 | on_update: t.Callable[[ds.HeaderSet], None] | None = None,
738 | ) -> ds.HeaderSet:
739 | """Parse a set-like header and return a
740 | :class:`~werkzeug.datastructures.HeaderSet` object:
741 |
742 | >>> hs = parse_set_header('token, "quoted value"')
743 |
744 | The return value is an object that treats the items case-insensitively
745 | and keeps the order of the items:
746 |
747 | >>> 'TOKEN' in hs
748 | True
749 | >>> hs.index('quoted value')
750 | 1
751 | >>> hs
752 | HeaderSet(['token', 'quoted value'])
753 |
754 | To create a header from the :class:`HeaderSet` again, use the
755 | :func:`dump_header` function.
756 |
757 | :param value: a set header to be parsed.
758 | :param on_update: an optional callable that is called every time a
759 | value on the :class:`~werkzeug.datastructures.HeaderSet`
760 | object is changed.
761 | :return: a :class:`~werkzeug.datastructures.HeaderSet`
762 | """
763 | if not value:
764 | return ds.HeaderSet(None, on_update)
765 | return ds.HeaderSet(parse_list_header(value), on_update)
766 |
767 |
768 | def parse_if_range_header(value: str | None) -> ds.IfRange:
769 | """Parses an if-range header which can be an etag or a date. Returns
770 | a :class:`~werkzeug.datastructures.IfRange` object.
771 |
772 | .. versionchanged:: 2.0
773 | If the value represents a datetime, it is timezone-aware.
774 |
775 | .. versionadded:: 0.7
776 | """
777 | if not value:
778 | return ds.IfRange()
779 | date = parse_date(value)
780 | if date is not None:
781 | return ds.IfRange(date=date)
782 | # drop weakness information
783 | return ds.IfRange(unquote_etag(value)[0])
784 |
785 |
786 | def parse_range_header(
787 | value: str | None, make_inclusive: bool = True
788 | ) -> ds.Range | None:
789 | """Parses a range header into a :class:`~werkzeug.datastructures.Range`
790 | object. If the header is missing or malformed `None` is returned.
791 | `ranges` is a list of ``(start, stop)`` tuples where the ranges are
792 | non-inclusive.
793 |
794 | .. versionadded:: 0.7
795 | """
796 | if not value or "=" not in value:
797 | return None
798 |
799 | ranges = []
800 | last_end = 0
801 | units, rng = value.split("=", 1)
802 | units = units.strip().lower()
803 |
804 | for item in rng.split(","):
805 | item = item.strip()
806 | if "-" not in item:
807 | return None
808 | if item.startswith("-"):
809 | if last_end < 0:
810 | return None
811 | try:
812 | begin = _plain_int(item)
813 | except ValueError:
814 | return None
815 | end = None
816 | last_end = -1
817 | elif "-" in item:
818 | begin_str, end_str = item.split("-", 1)
819 | begin_str = begin_str.strip()
820 | end_str = end_str.strip()
821 |
822 | try:
823 | begin = _plain_int(begin_str)
824 | except ValueError:
825 | return None
826 |
827 | if begin < last_end or last_end < 0:
828 | return None
829 | if end_str:
830 | try:
831 | end = _plain_int(end_str) + 1
832 | except ValueError:
833 | return None
834 |
835 | if begin >= end:
836 | return None
837 | else:
838 | end = None
839 | last_end = end if end is not None else -1
840 | ranges.append((begin, end))
841 |
842 | return ds.Range(units, ranges)
843 |
844 |
845 | def parse_content_range_header(
846 | value: str | None,
847 | on_update: t.Callable[[ds.ContentRange], None] | None = None,
848 | ) -> ds.ContentRange | None:
849 | """Parses a range header into a
850 | :class:`~werkzeug.datastructures.ContentRange` object or `None` if
851 | parsing is not possible.
852 |
853 | .. versionadded:: 0.7
854 |
855 | :param value: a content range header to be parsed.
856 | :param on_update: an optional callable that is called every time a value
857 | on the :class:`~werkzeug.datastructures.ContentRange`
858 | object is changed.
859 | """
860 | if value is None:
861 | return None
862 | try:
863 | units, rangedef = (value or "").strip().split(None, 1)
864 | except ValueError:
865 | return None
866 |
867 | if "/" not in rangedef:
868 | return None
869 | rng, length_str = rangedef.split("/", 1)
870 | if length_str == "*":
871 | length = None
872 | else:
873 | try:
874 | length = _plain_int(length_str)
875 | except ValueError:
876 | return None
877 |
878 | if rng == "*":
879 | if not is_byte_range_valid(None, None, length):
880 | return None
881 |
882 | return ds.ContentRange(units, None, None, length, on_update=on_update)
883 | elif "-" not in rng:
884 | return None
885 |
886 | start_str, stop_str = rng.split("-", 1)
887 | try:
888 | start = _plain_int(start_str)
889 | stop = _plain_int(stop_str) + 1
890 | except ValueError:
891 | return None
892 |
893 | if is_byte_range_valid(start, stop, length):
894 | return ds.ContentRange(units, start, stop, length, on_update=on_update)
895 |
896 | return None
897 |
898 |
899 | def quote_etag(etag: str, weak: bool = False) -> str:
900 | """Quote an etag.
901 |
902 | :param etag: the etag to quote.
903 | :param weak: set to `True` to tag it "weak".
904 | """
905 | if '"' in etag:
906 | raise ValueError("invalid etag")
907 | etag = f'"{etag}"'
908 | if weak:
909 | etag = f"W/{etag}"
910 | return etag
911 |
912 |
913 | def unquote_etag(
914 | etag: str | None,
915 | ) -> tuple[str, bool] | tuple[None, None]:
916 | """Unquote a single etag:
917 |
918 | >>> unquote_etag('W/"bar"')
919 | ('bar', True)
920 | >>> unquote_etag('"bar"')
921 | ('bar', False)
922 |
923 | :param etag: the etag identifier to unquote.
924 | :return: a ``(etag, weak)`` tuple.
925 | """
926 | if not etag:
927 | return None, None
928 | etag = etag.strip()
929 | weak = False
930 | if etag.startswith(("W/", "w/")):
931 | weak = True
932 | etag = etag[2:]
933 | if etag[:1] == etag[-1:] == '"':
934 | etag = etag[1:-1]
935 | return etag, weak
936 |
937 |
938 | def parse_etags(value: str | None) -> ds.ETags:
939 | """Parse an etag header.
940 |
941 | :param value: the tag header to parse
942 | :return: an :class:`~werkzeug.datastructures.ETags` object.
943 | """
944 | if not value:
945 | return ds.ETags()
946 | strong = []
947 | weak = []
948 | end = len(value)
949 | pos = 0
950 | while pos < end:
951 | match = _etag_re.match(value, pos)
952 | if match is None:
953 | break
954 | is_weak, quoted, raw = match.groups()
955 | if raw == "*":
956 | return ds.ETags(star_tag=True)
957 | elif quoted:
958 | raw = quoted
959 | if is_weak:
960 | weak.append(raw)
961 | else:
962 | strong.append(raw)
963 | pos = match.end()
964 | return ds.ETags(strong, weak)
965 |
966 |
967 | def generate_etag(data: bytes) -> str:
968 | """Generate an etag for some data.
969 |
970 | .. versionchanged:: 2.0
971 | Use SHA-1. MD5 may not be available in some environments.
972 | """
973 | return sha1(data).hexdigest()
974 |
975 |
976 | def parse_date(value: str | None) -> datetime | None:
977 | """Parse an :rfc:`2822` date into a timezone-aware
978 | :class:`datetime.datetime` object, or ``None`` if parsing fails.
979 |
980 | This is a wrapper for :func:`email.utils.parsedate_to_datetime`. It
981 | returns ``None`` if parsing fails instead of raising an exception,
982 | and always returns a timezone-aware datetime object. If the string
983 | doesn't have timezone information, it is assumed to be UTC.
984 |
985 | :param value: A string with a supported date format.
986 |
987 | .. versionchanged:: 2.0
988 | Return a timezone-aware datetime object. Use
989 | ``email.utils.parsedate_to_datetime``.
990 | """
991 | if value is None:
992 | return None
993 |
994 | try:
995 | dt = email.utils.parsedate_to_datetime(value)
996 | except (TypeError, ValueError):
997 | return None
998 |
999 | if dt.tzinfo is None:
1000 | return dt.replace(tzinfo=timezone.utc)
1001 |
1002 | return dt
1003 |
1004 |
1005 | def http_date(
1006 | timestamp: datetime | date | int | float | struct_time | None = None,
1007 | ) -> str:
1008 | """Format a datetime object or timestamp into an :rfc:`2822` date
1009 | string.
1010 |
1011 | This is a wrapper for :func:`email.utils.format_datetime`. It
1012 | assumes naive datetime objects are in UTC instead of raising an
1013 | exception.
1014 |
1015 | :param timestamp: The datetime or timestamp to format. Defaults to
1016 | the current time.
1017 |
1018 | .. versionchanged:: 2.0
1019 | Use ``email.utils.format_datetime``. Accept ``date`` objects.
1020 | """
1021 | if isinstance(timestamp, date):
1022 | if not isinstance(timestamp, datetime):
1023 | # Assume plain date is midnight UTC.
1024 | timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc)
1025 | else:
1026 | # Ensure datetime is timezone-aware.
1027 | timestamp = _dt_as_utc(timestamp)
1028 |
1029 | return email.utils.format_datetime(timestamp, usegmt=True)
1030 |
1031 | if isinstance(timestamp, struct_time):
1032 | timestamp = mktime(timestamp)
1033 |
1034 | return email.utils.formatdate(timestamp, usegmt=True)
1035 |
1036 |
1037 | def parse_age(value: str | None = None) -> timedelta | None:
1038 | """Parses a base-10 integer count of seconds into a timedelta.
1039 |
1040 | If parsing fails, the return value is `None`.
1041 |
1042 | :param value: a string consisting of an integer represented in base-10
1043 | :return: a :class:`datetime.timedelta` object or `None`.
1044 | """
1045 | if not value:
1046 | return None
1047 | try:
1048 | seconds = int(value)
1049 | except ValueError:
1050 | return None
1051 | if seconds < 0:
1052 | return None
1053 | try:
1054 | return timedelta(seconds=seconds)
1055 | except OverflowError:
1056 | return None
1057 |
1058 |
1059 | def dump_age(age: timedelta | int | None = None) -> str | None:
1060 | """Formats the duration as a base-10 integer.
1061 |
1062 | :param age: should be an integer number of seconds,
1063 | a :class:`datetime.timedelta` object, or,
1064 | if the age is unknown, `None` (default).
1065 | """
1066 | if age is None:
1067 | return None
1068 | if isinstance(age, timedelta):
1069 | age = int(age.total_seconds())
1070 | else:
1071 | age = int(age)
1072 |
1073 | if age < 0:
1074 | raise ValueError("age cannot be negative")
1075 |
1076 | return str(age)
1077 |
1078 |
1079 | def is_resource_modified(
1080 | environ: WSGIEnvironment,
1081 | etag: str | None = None,
1082 | data: bytes | None = None,
1083 | last_modified: datetime | str | None = None,
1084 | ignore_if_range: bool = True,
1085 | ) -> bool:
1086 | """Convenience method for conditional requests.
1087 |
1088 | :param environ: the WSGI environment of the request to be checked.
1089 | :param etag: the etag for the response for comparison.
1090 | :param data: or alternatively the data of the response to automatically
1091 | generate an etag using :func:`generate_etag`.
1092 | :param last_modified: an optional date of the last modification.
1093 | :param ignore_if_range: If `False`, `If-Range` header will be taken into
1094 | account.
1095 | :return: `True` if the resource was modified, otherwise `False`.
1096 |
1097 | .. versionchanged:: 2.0
1098 | SHA-1 is used to generate an etag value for the data. MD5 may
1099 | not be available in some environments.
1100 |
1101 | .. versionchanged:: 1.0.0
1102 | The check is run for methods other than ``GET`` and ``HEAD``.
1103 | """
1104 | return _sansio_http.is_resource_modified(
1105 | http_range=environ.get("HTTP_RANGE"),
1106 | http_if_range=environ.get("HTTP_IF_RANGE"),
1107 | http_if_modified_since=environ.get("HTTP_IF_MODIFIED_SINCE"),
1108 | http_if_none_match=environ.get("HTTP_IF_NONE_MATCH"),
1109 | http_if_match=environ.get("HTTP_IF_MATCH"),
1110 | etag=etag,
1111 | data=data,
1112 | last_modified=last_modified,
1113 | ignore_if_range=ignore_if_range,
1114 | )
1115 |
1116 |
1117 | def remove_entity_headers(
1118 | headers: ds.Headers | list[tuple[str, str]],
1119 | allowed: t.Iterable[str] = ("expires", "content-location"),
1120 | ) -> None:
1121 | """Remove all entity headers from a list or :class:`Headers` object. This
1122 | operation works in-place. `Expires` and `Content-Location` headers are
1123 | by default not removed. The reason for this is :rfc:`2616` section
1124 | 10.3.5 which specifies some entity headers that should be sent.
1125 |
1126 | .. versionchanged:: 0.5
1127 | added `allowed` parameter.
1128 |
1129 | :param headers: a list or :class:`Headers` object.
1130 | :param allowed: a list of headers that should still be allowed even though
1131 | they are entity headers.
1132 | """
1133 | allowed = {x.lower() for x in allowed}
1134 | headers[:] = [
1135 | (key, value)
1136 | for key, value in headers
1137 | if not is_entity_header(key) or key.lower() in allowed
1138 | ]
1139 |
1140 |
1141 | def remove_hop_by_hop_headers(headers: ds.Headers | list[tuple[str, str]]) -> None:
1142 | """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or
1143 | :class:`Headers` object. This operation works in-place.
1144 |
1145 | .. versionadded:: 0.5
1146 |
1147 | :param headers: a list or :class:`Headers` object.
1148 | """
1149 | headers[:] = [
1150 | (key, value) for key, value in headers if not is_hop_by_hop_header(key)
1151 | ]
1152 |
1153 |
1154 | def is_entity_header(header: str) -> bool:
1155 | """Check if a header is an entity header.
1156 |
1157 | .. versionadded:: 0.5
1158 |
1159 | :param header: the header to test.
1160 | :return: `True` if it's an entity header, `False` otherwise.
1161 | """
1162 | return header.lower() in _entity_headers
1163 |
1164 |
1165 | def is_hop_by_hop_header(header: str) -> bool:
1166 | """Check if a header is an HTTP/1.1 "Hop-by-Hop" header.
1167 |
1168 | .. versionadded:: 0.5
1169 |
1170 | :param header: the header to test.
1171 | :return: `True` if it's an HTTP/1.1 "Hop-by-Hop" header, `False` otherwise.
1172 | """
1173 | return header.lower() in _hop_by_hop_headers
1174 |
1175 |
1176 | def parse_cookie(
1177 | header: WSGIEnvironment | str | None,
1178 | cls: type[ds.MultiDict[str, str]] | None = None,
1179 | ) -> ds.MultiDict[str, str]:
1180 | """Parse a cookie from a string or WSGI environ.
1181 |
1182 | The same key can be provided multiple times, the values are stored
1183 | in-order. The default :class:`MultiDict` will have the first value
1184 | first, and all values can be retrieved with
1185 | :meth:`MultiDict.getlist`.
1186 |
1187 | :param header: The cookie header as a string, or a WSGI environ dict
1188 | with a ``HTTP_COOKIE`` key.
1189 | :param cls: A dict-like class to store the parsed cookies in.
1190 | Defaults to :class:`MultiDict`.
1191 |
1192 | .. versionchanged:: 3.0
1193 | Passing bytes, and the ``charset`` and ``errors`` parameters, were removed.
1194 |
1195 | .. versionchanged:: 1.0
1196 | Returns a :class:`MultiDict` instead of a ``TypeConversionDict``.
1197 |
1198 | .. versionchanged:: 0.5
1199 | Returns a :class:`TypeConversionDict` instead of a regular dict. The ``cls``
1200 | parameter was added.
1201 | """
1202 | if isinstance(header, dict):
1203 | cookie = header.get("HTTP_COOKIE")
1204 | else:
1205 | cookie = header
1206 |
1207 | if cookie:
1208 | cookie = cookie.encode("latin1").decode()
1209 |
1210 | return _sansio_http.parse_cookie(cookie=cookie, cls=cls)
1211 |
1212 |
1213 | _cookie_no_quote_re = re.compile(r"[\w!#$%&'()*+\-./:<=>?@\[\]^`{|}~]*", re.A)
1214 | _cookie_slash_re = re.compile(rb"[\x00-\x19\",;\\\x7f-\xff]", re.A)
1215 | _cookie_slash_map = {b'"': b'\\"', b"\\": b"\\\\"}
1216 | _cookie_slash_map.update(
1217 | (v.to_bytes(1, "big"), b"\\%03o" % v)
1218 | for v in [*range(0x20), *b",;", *range(0x7F, 256)]
1219 | )
1220 |
1221 |
1222 | def dump_cookie(
1223 | key: str,
1224 | value: str = "",
1225 | max_age: timedelta | int | None = None,
1226 | expires: str | datetime | int | float | None = None,
1227 | path: str | None = "/",
1228 | domain: str | None = None,
1229 | secure: bool = False,
1230 | httponly: bool = False,
1231 | sync_expires: bool = True,
1232 | max_size: int = 4093,
1233 | samesite: str | None = None,
1234 | ) -> str:
1235 | """Create a Set-Cookie header without the ``Set-Cookie`` prefix.
1236 |
1237 | The return value is usually restricted to ascii as the vast majority
1238 | of values are properly escaped, but that is no guarantee. It's
1239 | tunneled through latin1 as required by :pep:`3333`.
1240 |
1241 | The return value is not ASCII safe if the key contains unicode
1242 | characters. This is technically against the specification but
1243 | happens in the wild. It's strongly recommended to not use
1244 | non-ASCII values for the keys.
1245 |
1246 | :param max_age: should be a number of seconds, or `None` (default) if
1247 | the cookie should last only as long as the client's
1248 | browser session. Additionally `timedelta` objects
1249 | are accepted, too.
1250 | :param expires: should be a `datetime` object or unix timestamp.
1251 | :param path: limits the cookie to a given path, per default it will
1252 | span the whole domain.
1253 | :param domain: Use this if you want to set a cross-domain cookie. For
1254 | example, ``domain="example.com"`` will set a cookie
1255 | that is readable by the domain ``www.example.com``,
1256 | ``foo.example.com`` etc. Otherwise, a cookie will only
1257 | be readable by the domain that set it.
1258 | :param secure: The cookie will only be available via HTTPS
1259 | :param httponly: disallow JavaScript to access the cookie. This is an
1260 | extension to the cookie standard and probably not
1261 | supported by all browsers.
1262 | :param charset: the encoding for string values.
1263 | :param sync_expires: automatically set expires if max_age is defined
1264 | but expires not.
1265 | :param max_size: Warn if the final header value exceeds this size. The
1266 | default, 4093, should be safely `supported by most browsers
1267 | <cookie_>`_. Set to 0 to disable this check.
1268 | :param samesite: Limits the scope of the cookie such that it will
1269 | only be attached to requests if those requests are same-site.
1270 |
1271 | .. _`cookie`: http://browsercookielimits.squawky.net/
1272 |
1273 | .. versionchanged:: 3.0
1274 | Passing bytes, and the ``charset`` parameter, were removed.
1275 |
1276 | .. versionchanged:: 2.3.3
1277 | The ``path`` parameter is ``/`` by default.
1278 |
1279 | .. versionchanged:: 2.3.1
1280 | The value allows more characters without quoting.
1281 |
1282 | .. versionchanged:: 2.3
1283 | ``localhost`` and other names without a dot are allowed for the domain. A
1284 | leading dot is ignored.
1285 |
1286 | .. versionchanged:: 2.3
1287 | The ``path`` parameter is ``None`` by default.
1288 |
1289 | .. versionchanged:: 1.0.0
1290 | The string ``'None'`` is accepted for ``samesite``.
1291 | """
1292 | if path is not None:
1293 | # safe = https://url.spec.whatwg.org/#url-path-segment-string
1294 | # as well as percent for things that are already quoted
1295 | # excluding semicolon since it's part of the header syntax
1296 | path = quote(path, safe="%!$&'()*+,/:=@")
1297 |
1298 | if domain:
1299 | domain = domain.partition(":")[0].lstrip(".").encode("idna").decode("ascii")
1300 |
1301 | if isinstance(max_age, timedelta):
1302 | max_age = int(max_age.total_seconds())
1303 |
1304 | if expires is not None:
1305 | if not isinstance(expires, str):
1306 | expires = http_date(expires)
1307 | elif max_age is not None and sync_expires:
1308 | expires = http_date(datetime.now(tz=timezone.utc).timestamp() + max_age)
1309 |
1310 | if samesite is not None:
1311 | samesite = samesite.title()
1312 |
1313 | if samesite not in {"Strict", "Lax", "None"}:
1314 | raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.")
1315 |
1316 | # Quote value if it contains characters not allowed by RFC 6265. Slash-escape with
1317 | # three octal digits, which matches http.cookies, although the RFC suggests base64.
1318 | if not _cookie_no_quote_re.fullmatch(value):
1319 | # Work with bytes here, since a UTF-8 character could be multiple bytes.
1320 | value = _cookie_slash_re.sub(
1321 | lambda m: _cookie_slash_map[m.group()], value.encode()
1322 | ).decode("ascii")
1323 | value = f'"{value}"'
1324 |
1325 | # Send a non-ASCII key as mojibake. Everything else should already be ASCII.
1326 | # TODO Remove encoding dance, it seems like clients accept UTF-8 keys
1327 | buf = [f"{key.encode().decode('latin1')}={value}"]
1328 |
1329 | for k, v in (
1330 | ("Domain", domain),
1331 | ("Expires", expires),
1332 | ("Max-Age", max_age),
1333 | ("Secure", secure),
1334 | ("HttpOnly", httponly),
1335 | ("Path", path),
1336 | ("SameSite", samesite),
1337 | ):
1338 | if v is None or v is False:
1339 | continue
1340 |
1341 | if v is True:
1342 | buf.append(k)
1343 | continue
1344 |
1345 | buf.append(f"{k}={v}")
1346 |
1347 | rv = "; ".join(buf)
1348 |
1349 | # Warn if the final value of the cookie is larger than the limit. If the cookie is
1350 | # too large, then it may be silently ignored by the browser, which can be quite hard
1351 | # to debug.
1352 | cookie_size = len(rv)
1353 |
1354 | if max_size and cookie_size > max_size:
1355 | value_size = len(value)
1356 | warnings.warn(
1357 | f"The '{key}' cookie is too large: the value was {value_size} bytes but the"
1358 | f" header required {cookie_size - value_size} extra bytes. The final size"
1359 | f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may"
1360 | " silently ignore cookies larger than this.",
1361 | stacklevel=2,
1362 | )
1363 |
1364 | return rv
1365 |
1366 |
1367 | def is_byte_range_valid(
1368 | start: int | None, stop: int | None, length: int | None
1369 | ) -> bool:
1370 | """Checks if a given byte content range is valid for the given length.
1371 |
1372 | .. versionadded:: 0.7
1373 | """
1374 | if (start is None) != (stop is None):
1375 | return False
1376 | elif start is None:
1377 | return length is None or length >= 0
1378 | elif length is None:
1379 | return 0 <= start < stop # type: ignore
1380 | elif start >= stop: # type: ignore
1381 | return False
1382 | return 0 <= start < length
1383 |
1384 |
1385 | # circular dependencies
1386 | from . import datastructures as ds
1387 | from .sansio import http as _sansio_http
1388 |
```