This is page 95 of 168. Use http://codebase.md/romanshablio/mcp_server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .DS_Store
├── .venv
│ ├── __pycache__
│ │ └── hello.cpython-312.pyc
│ ├── bin
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── Activate.ps1
│ │ ├── flask
│ │ ├── normalizer
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.12
│ │ ├── python
│ │ ├── python3
│ │ └── python3.12
│ ├── hello.py
│ ├── lib
│ │ └── python3.12
│ │ └── site-packages
│ │ ├── beautifulsoup4-4.12.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ ├── AUTHORS
│ │ │ │ └── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── blinker
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _utilities.cpython-312.pyc
│ │ │ │ └── base.cpython-312.pyc
│ │ │ ├── _utilities.py
│ │ │ ├── base.py
│ │ │ └── py.typed
│ │ ├── blinker-1.8.2.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── bs4
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── css.cpython-312.pyc
│ │ │ │ ├── dammit.cpython-312.pyc
│ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ ├── element.cpython-312.pyc
│ │ │ │ └── formatter.cpython-312.pyc
│ │ │ ├── builder
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _html5lib.cpython-312.pyc
│ │ │ │ │ ├── _htmlparser.cpython-312.pyc
│ │ │ │ │ └── _lxml.cpython-312.pyc
│ │ │ │ ├── _html5lib.py
│ │ │ │ ├── _htmlparser.py
│ │ │ │ └── _lxml.py
│ │ │ ├── css.py
│ │ │ ├── dammit.py
│ │ │ ├── diagnose.py
│ │ │ ├── element.py
│ │ │ ├── formatter.py
│ │ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── test_builder_registry.cpython-312.pyc
│ │ │ │ ├── test_builder.cpython-312.pyc
│ │ │ │ ├── test_css.cpython-312.pyc
│ │ │ │ ├── test_dammit.cpython-312.pyc
│ │ │ │ ├── test_docs.cpython-312.pyc
│ │ │ │ ├── test_element.cpython-312.pyc
│ │ │ │ ├── test_formatter.cpython-312.pyc
│ │ │ │ ├── test_fuzz.cpython-312.pyc
│ │ │ │ ├── test_html5lib.cpython-312.pyc
│ │ │ │ ├── test_htmlparser.cpython-312.pyc
│ │ │ │ ├── test_lxml.cpython-312.pyc
│ │ │ │ ├── test_navigablestring.cpython-312.pyc
│ │ │ │ ├── test_pageelement.cpython-312.pyc
│ │ │ │ ├── test_soup.cpython-312.pyc
│ │ │ │ ├── test_tag.cpython-312.pyc
│ │ │ │ └── test_tree.cpython-312.pyc
│ │ │ ├── fuzz
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase
│ │ │ │ ├── crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase
│ │ │ │ └── crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase
│ │ │ ├── test_builder_registry.py
│ │ │ ├── test_builder.py
│ │ │ ├── test_css.py
│ │ │ ├── test_dammit.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_element.py
│ │ │ ├── test_formatter.py
│ │ │ ├── test_fuzz.py
│ │ │ ├── test_html5lib.py
│ │ │ ├── test_htmlparser.py
│ │ │ ├── test_lxml.py
│ │ │ ├── test_navigablestring.py
│ │ │ ├── test_pageelement.py
│ │ │ ├── test_soup.py
│ │ │ ├── test_tag.py
│ │ │ └── test_tree.py
│ │ ├── certifi
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── core.cpython-312.pyc
│ │ │ ├── cacert.pem
│ │ │ ├── core.py
│ │ │ └── py.typed
│ │ ├── certifi-2024.8.30.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── charset_normalizer
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── cd.cpython-312.pyc
│ │ │ │ ├── constant.cpython-312.pyc
│ │ │ │ ├── legacy.cpython-312.pyc
│ │ │ │ ├── md.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── version.cpython-312.pyc
│ │ │ ├── api.py
│ │ │ ├── cd.py
│ │ │ ├── cli
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── __main__.cpython-312.pyc
│ │ │ ├── constant.py
│ │ │ ├── legacy.py
│ │ │ ├── md__mypyc.cpython-312-darwin.so
│ │ │ ├── md.cpython-312-darwin.so
│ │ │ ├── md.py
│ │ │ ├── models.py
│ │ │ ├── py.typed
│ │ │ ├── utils.py
│ │ │ └── version.py
│ │ ├── charset_normalizer-3.4.0.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── click
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ ├── _termui_impl.cpython-312.pyc
│ │ │ │ ├── _textwrap.cpython-312.pyc
│ │ │ │ ├── _winconsole.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── decorators.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formatting.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── shell_completion.cpython-312.pyc
│ │ │ │ ├── termui.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── types.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── _compat.py
│ │ │ ├── _termui_impl.py
│ │ │ ├── _textwrap.py
│ │ │ ├── _winconsole.py
│ │ │ ├── core.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── formatting.py
│ │ │ ├── globals.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── shell_completion.py
│ │ │ ├── termui.py
│ │ │ ├── testing.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ ├── click-8.1.7.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.rst
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── fake_useragent
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ ├── fake.cpython-312.pyc
│ │ │ │ ├── log.cpython-312.pyc
│ │ │ │ ├── settings.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── data
│ │ │ │ └── browsers.json
│ │ │ ├── errors.py
│ │ │ ├── fake.py
│ │ │ ├── log.py
│ │ │ ├── settings.py
│ │ │ └── utils.py
│ │ ├── fake_useragent-1.5.1.dist-info
│ │ │ ├── AUTHORS
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── flask
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ ├── cli.cpython-312.pyc
│ │ │ │ ├── config.cpython-312.pyc
│ │ │ │ ├── ctx.cpython-312.pyc
│ │ │ │ ├── debughelpers.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── helpers.cpython-312.pyc
│ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── signals.cpython-312.pyc
│ │ │ │ ├── templating.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── typing.cpython-312.pyc
│ │ │ │ ├── views.cpython-312.pyc
│ │ │ │ └── wrappers.cpython-312.pyc
│ │ │ ├── app.py
│ │ │ ├── blueprints.py
│ │ │ ├── cli.py
│ │ │ ├── config.py
│ │ │ ├── ctx.py
│ │ │ ├── debughelpers.py
│ │ │ ├── globals.py
│ │ │ ├── helpers.py
│ │ │ ├── json
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ └── tag.cpython-312.pyc
│ │ │ │ ├── provider.py
│ │ │ │ └── tag.py
│ │ │ ├── logging.py
│ │ │ ├── py.typed
│ │ │ ├── sansio
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ │ └── scaffold.cpython-312.pyc
│ │ │ │ ├── app.py
│ │ │ │ ├── blueprints.py
│ │ │ │ ├── README.md
│ │ │ │ └── scaffold.py
│ │ │ ├── sessions.py
│ │ │ ├── signals.py
│ │ │ ├── templating.py
│ │ │ ├── testing.py
│ │ │ ├── typing.py
│ │ │ ├── views.py
│ │ │ └── wrappers.py
│ │ ├── flask-3.0.3.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── idna
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ ├── codec.py
│ │ │ ├── compat.py
│ │ │ ├── core.py
│ │ │ ├── idnadata.py
│ │ │ ├── intranges.py
│ │ │ ├── package_data.py
│ │ │ ├── py.typed
│ │ │ └── uts46data.py
│ │ ├── idna-3.10.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── itsdangerous
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ ├── exc.cpython-312.pyc
│ │ │ │ ├── serializer.cpython-312.pyc
│ │ │ │ ├── signer.cpython-312.pyc
│ │ │ │ ├── timed.cpython-312.pyc
│ │ │ │ └── url_safe.cpython-312.pyc
│ │ │ ├── _json.py
│ │ │ ├── encoding.py
│ │ │ ├── exc.py
│ │ │ ├── py.typed
│ │ │ ├── serializer.py
│ │ │ ├── signer.py
│ │ │ ├── timed.py
│ │ │ └── url_safe.py
│ │ ├── itsdangerous-2.2.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── jinja2
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _identifier.cpython-312.pyc
│ │ │ │ ├── async_utils.cpython-312.pyc
│ │ │ │ ├── bccache.cpython-312.pyc
│ │ │ │ ├── compiler.cpython-312.pyc
│ │ │ │ ├── constants.cpython-312.pyc
│ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ ├── defaults.cpython-312.pyc
│ │ │ │ ├── environment.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ ├── filters.cpython-312.pyc
│ │ │ │ ├── idtracking.cpython-312.pyc
│ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ ├── loaders.cpython-312.pyc
│ │ │ │ ├── meta.cpython-312.pyc
│ │ │ │ ├── nativetypes.cpython-312.pyc
│ │ │ │ ├── nodes.cpython-312.pyc
│ │ │ │ ├── optimizer.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── runtime.cpython-312.pyc
│ │ │ │ ├── sandbox.cpython-312.pyc
│ │ │ │ ├── tests.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── visitor.cpython-312.pyc
│ │ │ ├── _identifier.py
│ │ │ ├── async_utils.py
│ │ │ ├── bccache.py
│ │ │ ├── compiler.py
│ │ │ ├── constants.py
│ │ │ ├── debug.py
│ │ │ ├── defaults.py
│ │ │ ├── environment.py
│ │ │ ├── exceptions.py
│ │ │ ├── ext.py
│ │ │ ├── filters.py
│ │ │ ├── idtracking.py
│ │ │ ├── lexer.py
│ │ │ ├── loaders.py
│ │ │ ├── meta.py
│ │ │ ├── nativetypes.py
│ │ │ ├── nodes.py
│ │ │ ├── optimizer.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── runtime.py
│ │ │ ├── sandbox.py
│ │ │ ├── tests.py
│ │ │ ├── utils.py
│ │ │ └── visitor.py
│ │ ├── jinja2-3.1.4.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── lxml
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _elementpath.cpython-312.pyc
│ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ ├── cssselect.cpython-312.pyc
│ │ │ │ ├── doctestcompare.cpython-312.pyc
│ │ │ │ ├── ElementInclude.cpython-312.pyc
│ │ │ │ ├── pyclasslookup.cpython-312.pyc
│ │ │ │ ├── sax.cpython-312.pyc
│ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ ├── _elementpath.cpython-312-darwin.so
│ │ │ ├── _elementpath.py
│ │ │ ├── apihelpers.pxi
│ │ │ ├── builder.cpython-312-darwin.so
│ │ │ ├── builder.py
│ │ │ ├── classlookup.pxi
│ │ │ ├── cleanup.pxi
│ │ │ ├── cssselect.py
│ │ │ ├── debug.pxi
│ │ │ ├── docloader.pxi
│ │ │ ├── doctestcompare.py
│ │ │ ├── dtd.pxi
│ │ │ ├── ElementInclude.py
│ │ │ ├── etree_api.h
│ │ │ ├── etree.cpython-312-darwin.so
│ │ │ ├── etree.h
│ │ │ ├── etree.pyx
│ │ │ ├── extensions.pxi
│ │ │ ├── html
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _diffcommand.cpython-312.pyc
│ │ │ │ │ ├── _html5builder.cpython-312.pyc
│ │ │ │ │ ├── _setmixin.cpython-312.pyc
│ │ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ │ ├── clean.cpython-312.pyc
│ │ │ │ │ ├── defs.cpython-312.pyc
│ │ │ │ │ ├── diff.cpython-312.pyc
│ │ │ │ │ ├── ElementSoup.cpython-312.pyc
│ │ │ │ │ ├── formfill.cpython-312.pyc
│ │ │ │ │ ├── html5parser.cpython-312.pyc
│ │ │ │ │ ├── soupparser.cpython-312.pyc
│ │ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ │ ├── _diffcommand.py
│ │ │ │ ├── _html5builder.py
│ │ │ │ ├── _setmixin.py
│ │ │ │ ├── builder.py
│ │ │ │ ├── clean.py
│ │ │ │ ├── defs.py
│ │ │ │ ├── diff.cpython-312-darwin.so
│ │ │ │ ├── diff.py
│ │ │ │ ├── ElementSoup.py
│ │ │ │ ├── formfill.py
│ │ │ │ ├── html5parser.py
│ │ │ │ ├── soupparser.py
│ │ │ │ └── usedoctest.py
│ │ │ ├── includes
│ │ │ │ ├── __init__.pxd
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── c14n.pxd
│ │ │ │ ├── config.pxd
│ │ │ │ ├── dtdvalid.pxd
│ │ │ │ ├── etree_defs.h
│ │ │ │ ├── etreepublic.pxd
│ │ │ │ ├── extlibs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── libcharset.h
│ │ │ │ │ ├── localcharset.h
│ │ │ │ │ ├── zconf.h
│ │ │ │ │ └── zlib.h
│ │ │ │ ├── htmlparser.pxd
│ │ │ │ ├── libexslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── exslt.h
│ │ │ │ │ ├── exsltconfig.h
│ │ │ │ │ └── exsltexports.h
│ │ │ │ ├── libxml
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── c14n.h
│ │ │ │ │ ├── catalog.h
│ │ │ │ │ ├── chvalid.h
│ │ │ │ │ ├── debugXML.h
│ │ │ │ │ ├── dict.h
│ │ │ │ │ ├── encoding.h
│ │ │ │ │ ├── entities.h
│ │ │ │ │ ├── globals.h
│ │ │ │ │ ├── hash.h
│ │ │ │ │ ├── HTMLparser.h
│ │ │ │ │ ├── HTMLtree.h
│ │ │ │ │ ├── list.h
│ │ │ │ │ ├── nanoftp.h
│ │ │ │ │ ├── nanohttp.h
│ │ │ │ │ ├── parser.h
│ │ │ │ │ ├── parserInternals.h
│ │ │ │ │ ├── relaxng.h
│ │ │ │ │ ├── SAX.h
│ │ │ │ │ ├── SAX2.h
│ │ │ │ │ ├── schemasInternals.h
│ │ │ │ │ ├── schematron.h
│ │ │ │ │ ├── threads.h
│ │ │ │ │ ├── tree.h
│ │ │ │ │ ├── uri.h
│ │ │ │ │ ├── valid.h
│ │ │ │ │ ├── xinclude.h
│ │ │ │ │ ├── xlink.h
│ │ │ │ │ ├── xmlautomata.h
│ │ │ │ │ ├── xmlerror.h
│ │ │ │ │ ├── xmlexports.h
│ │ │ │ │ ├── xmlIO.h
│ │ │ │ │ ├── xmlmemory.h
│ │ │ │ │ ├── xmlmodule.h
│ │ │ │ │ ├── xmlreader.h
│ │ │ │ │ ├── xmlregexp.h
│ │ │ │ │ ├── xmlsave.h
│ │ │ │ │ ├── xmlschemas.h
│ │ │ │ │ ├── xmlschemastypes.h
│ │ │ │ │ ├── xmlstring.h
│ │ │ │ │ ├── xmlunicode.h
│ │ │ │ │ ├── xmlversion.h
│ │ │ │ │ ├── xmlwriter.h
│ │ │ │ │ ├── xpath.h
│ │ │ │ │ ├── xpathInternals.h
│ │ │ │ │ └── xpointer.h
│ │ │ │ ├── libxslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── attributes.h
│ │ │ │ │ ├── documents.h
│ │ │ │ │ ├── extensions.h
│ │ │ │ │ ├── extra.h
│ │ │ │ │ ├── functions.h
│ │ │ │ │ ├── imports.h
│ │ │ │ │ ├── keys.h
│ │ │ │ │ ├── namespaces.h
│ │ │ │ │ ├── numbersInternals.h
│ │ │ │ │ ├── pattern.h
│ │ │ │ │ ├── preproc.h
│ │ │ │ │ ├── security.h
│ │ │ │ │ ├── templates.h
│ │ │ │ │ ├── transform.h
│ │ │ │ │ ├── variables.h
│ │ │ │ │ ├── xslt.h
│ │ │ │ │ ├── xsltconfig.h
│ │ │ │ │ ├── xsltexports.h
│ │ │ │ │ ├── xsltInternals.h
│ │ │ │ │ ├── xsltlocale.h
│ │ │ │ │ └── xsltutils.h
│ │ │ │ ├── lxml-version.h
│ │ │ │ ├── relaxng.pxd
│ │ │ │ ├── schematron.pxd
│ │ │ │ ├── tree.pxd
│ │ │ │ ├── uri.pxd
│ │ │ │ ├── xinclude.pxd
│ │ │ │ ├── xmlerror.pxd
│ │ │ │ ├── xmlparser.pxd
│ │ │ │ ├── xmlschema.pxd
│ │ │ │ ├── xpath.pxd
│ │ │ │ └── xslt.pxd
│ │ │ ├── isoschematron
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ └── resources
│ │ │ │ ├── rng
│ │ │ │ │ └── iso-schematron.rng
│ │ │ │ └── xsl
│ │ │ │ ├── iso-schematron-xslt1
│ │ │ │ │ ├── iso_abstract_expand.xsl
│ │ │ │ │ ├── iso_dsdl_include.xsl
│ │ │ │ │ ├── iso_schematron_message.xsl
│ │ │ │ │ ├── iso_schematron_skeleton_for_xslt1.xsl
│ │ │ │ │ ├── iso_svrl_for_xslt1.xsl
│ │ │ │ │ └── readme.txt
│ │ │ │ ├── RNG2Schtrn.xsl
│ │ │ │ └── XSD2Schtrn.xsl
│ │ │ ├── iterparse.pxi
│ │ │ ├── lxml.etree_api.h
│ │ │ ├── lxml.etree.h
│ │ │ ├── nsclasses.pxi
│ │ │ ├── objectify.cpython-312-darwin.so
│ │ │ ├── objectify.pyx
│ │ │ ├── objectpath.pxi
│ │ │ ├── parser.pxi
│ │ │ ├── parsertarget.pxi
│ │ │ ├── proxy.pxi
│ │ │ ├── public-api.pxi
│ │ │ ├── pyclasslookup.py
│ │ │ ├── readonlytree.pxi
│ │ │ ├── relaxng.pxi
│ │ │ ├── sax.cpython-312-darwin.so
│ │ │ ├── sax.py
│ │ │ ├── saxparser.pxi
│ │ │ ├── schematron.pxi
│ │ │ ├── serializer.pxi
│ │ │ ├── usedoctest.py
│ │ │ ├── xinclude.pxi
│ │ │ ├── xmlerror.pxi
│ │ │ ├── xmlid.pxi
│ │ │ ├── xmlschema.pxi
│ │ │ ├── xpath.pxi
│ │ │ ├── xslt.pxi
│ │ │ └── xsltext.pxi
│ │ ├── lxml-5.3.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── LICENSES.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── markupsafe
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── _native.cpython-312.pyc
│ │ │ ├── _native.py
│ │ │ ├── _speedups.c
│ │ │ ├── _speedups.cpython-312-darwin.so
│ │ │ ├── _speedups.pyi
│ │ │ └── py.typed
│ │ ├── MarkupSafe-3.0.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── pip
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pip-runner__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── __pip-runner__.cpython-312.pyc
│ │ │ ├── _internal
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── build_env.cpython-312.pyc
│ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ ├── pyproject.cpython-312.pyc
│ │ │ │ │ ├── self_outdated_check.cpython-312.pyc
│ │ │ │ │ └── wheel_builder.cpython-312.pyc
│ │ │ │ ├── build_env.py
│ │ │ │ ├── cache.py
│ │ │ │ ├── cli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── autocompletion.cpython-312.pyc
│ │ │ │ │ │ ├── base_command.cpython-312.pyc
│ │ │ │ │ │ ├── cmdoptions.cpython-312.pyc
│ │ │ │ │ │ ├── command_context.cpython-312.pyc
│ │ │ │ │ │ ├── index_command.cpython-312.pyc
│ │ │ │ │ │ ├── main_parser.cpython-312.pyc
│ │ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bars.cpython-312.pyc
│ │ │ │ │ │ ├── req_command.cpython-312.pyc
│ │ │ │ │ │ ├── spinners.cpython-312.pyc
│ │ │ │ │ │ └── status_codes.cpython-312.pyc
│ │ │ │ │ ├── autocompletion.py
│ │ │ │ │ ├── base_command.py
│ │ │ │ │ ├── cmdoptions.py
│ │ │ │ │ ├── command_context.py
│ │ │ │ │ ├── index_command.py
│ │ │ │ │ ├── main_parser.py
│ │ │ │ │ ├── main.py
│ │ │ │ │ ├── parser.py
│ │ │ │ │ ├── progress_bars.py
│ │ │ │ │ ├── req_command.py
│ │ │ │ │ ├── spinners.py
│ │ │ │ │ └── status_codes.py
│ │ │ │ ├── commands
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── completion.cpython-312.pyc
│ │ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ ├── hash.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── inspect.cpython-312.pyc
│ │ │ │ │ │ ├── install.cpython-312.pyc
│ │ │ │ │ │ ├── list.cpython-312.pyc
│ │ │ │ │ │ ├── search.cpython-312.pyc
│ │ │ │ │ │ ├── show.cpython-312.pyc
│ │ │ │ │ │ ├── uninstall.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── completion.py
│ │ │ │ │ ├── configuration.py
│ │ │ │ │ ├── debug.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── hash.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── inspect.py
│ │ │ │ │ ├── install.py
│ │ │ │ │ ├── list.py
│ │ │ │ │ ├── search.py
│ │ │ │ │ ├── show.py
│ │ │ │ │ ├── uninstall.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── configuration.py
│ │ │ │ ├── distributions
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── installed.cpython-312.pyc
│ │ │ │ │ │ ├── sdist.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── installed.py
│ │ │ │ │ ├── sdist.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── index
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── collector.cpython-312.pyc
│ │ │ │ │ │ ├── package_finder.cpython-312.pyc
│ │ │ │ │ │ └── sources.cpython-312.pyc
│ │ │ │ │ ├── collector.py
│ │ │ │ │ ├── package_finder.py
│ │ │ │ │ └── sources.py
│ │ │ │ ├── locations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _distutils.cpython-312.pyc
│ │ │ │ │ │ ├── _sysconfig.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── _distutils.py
│ │ │ │ │ ├── _sysconfig.py
│ │ │ │ │ └── base.py
│ │ │ │ ├── main.py
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ └── pkg_resources.cpython-312.pyc
│ │ │ │ │ ├── _json.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── importlib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ │ ├── _dists.cpython-312.pyc
│ │ │ │ │ │ │ └── _envs.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.py
│ │ │ │ │ │ ├── _dists.py
│ │ │ │ │ │ └── _envs.py
│ │ │ │ │ └── pkg_resources.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── candidate.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url.cpython-312.pyc
│ │ │ │ │ │ ├── format_control.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── installation_report.cpython-312.pyc
│ │ │ │ │ │ ├── link.cpython-312.pyc
│ │ │ │ │ │ ├── scheme.cpython-312.pyc
│ │ │ │ │ │ ├── search_scope.cpython-312.pyc
│ │ │ │ │ │ ├── selection_prefs.cpython-312.pyc
│ │ │ │ │ │ ├── target_python.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── candidate.py
│ │ │ │ │ ├── direct_url.py
│ │ │ │ │ ├── format_control.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── installation_report.py
│ │ │ │ │ ├── link.py
│ │ │ │ │ ├── scheme.py
│ │ │ │ │ ├── search_scope.py
│ │ │ │ │ ├── selection_prefs.py
│ │ │ │ │ ├── target_python.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── network
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── lazy_wheel.cpython-312.pyc
│ │ │ │ │ │ ├── session.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── xmlrpc.cpython-312.pyc
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── lazy_wheel.py
│ │ │ │ │ ├── session.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── xmlrpc.py
│ │ │ │ ├── operations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ └── prepare.cpython-312.pyc
│ │ │ │ │ ├── build
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── build_tracker.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_legacy.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── build_tracker.py
│ │ │ │ │ │ ├── metadata_editable.py
│ │ │ │ │ │ ├── metadata_legacy.py
│ │ │ │ │ │ ├── metadata.py
│ │ │ │ │ │ ├── wheel_editable.py
│ │ │ │ │ │ ├── wheel_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── install
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── editable_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── editable_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ └── prepare.py
│ │ │ │ ├── pyproject.py
│ │ │ │ ├── req
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── constructors.cpython-312.pyc
│ │ │ │ │ │ ├── req_file.cpython-312.pyc
│ │ │ │ │ │ ├── req_install.cpython-312.pyc
│ │ │ │ │ │ ├── req_set.cpython-312.pyc
│ │ │ │ │ │ └── req_uninstall.cpython-312.pyc
│ │ │ │ │ ├── constructors.py
│ │ │ │ │ ├── req_file.py
│ │ │ │ │ ├── req_install.py
│ │ │ │ │ ├── req_set.py
│ │ │ │ │ └── req_uninstall.py
│ │ │ │ ├── resolution
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ │ └── resolver.py
│ │ │ │ │ └── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── candidates.cpython-312.pyc
│ │ │ │ │ │ ├── factory.cpython-312.pyc
│ │ │ │ │ │ ├── found_candidates.cpython-312.pyc
│ │ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ │ ├── reporter.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── candidates.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── found_candidates.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ ├── reporter.py
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ └── resolver.py
│ │ │ │ ├── self_outdated_check.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _jaraco_text.cpython-312.pyc
│ │ │ │ │ │ ├── _log.cpython-312.pyc
│ │ │ │ │ │ ├── appdirs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── compatibility_tags.cpython-312.pyc
│ │ │ │ │ │ ├── datetime.cpython-312.pyc
│ │ │ │ │ │ ├── deprecation.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url_helpers.cpython-312.pyc
│ │ │ │ │ │ ├── egg_link.cpython-312.pyc
│ │ │ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ │ │ ├── entrypoints.cpython-312.pyc
│ │ │ │ │ │ ├── filesystem.cpython-312.pyc
│ │ │ │ │ │ ├── filetypes.cpython-312.pyc
│ │ │ │ │ │ ├── glibc.cpython-312.pyc
│ │ │ │ │ │ ├── hashes.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── misc.cpython-312.pyc
│ │ │ │ │ │ ├── packaging.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── setuptools_build.cpython-312.pyc
│ │ │ │ │ │ ├── subprocess.cpython-312.pyc
│ │ │ │ │ │ ├── temp_dir.cpython-312.pyc
│ │ │ │ │ │ ├── unpacking.cpython-312.pyc
│ │ │ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ │ │ ├── virtualenv.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── _jaraco_text.py
│ │ │ │ │ ├── _log.py
│ │ │ │ │ ├── appdirs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── compatibility_tags.py
│ │ │ │ │ ├── datetime.py
│ │ │ │ │ ├── deprecation.py
│ │ │ │ │ ├── direct_url_helpers.py
│ │ │ │ │ ├── egg_link.py
│ │ │ │ │ ├── encoding.py
│ │ │ │ │ ├── entrypoints.py
│ │ │ │ │ ├── filesystem.py
│ │ │ │ │ ├── filetypes.py
│ │ │ │ │ ├── glibc.py
│ │ │ │ │ ├── hashes.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── misc.py
│ │ │ │ │ ├── packaging.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── setuptools_build.py
│ │ │ │ │ ├── subprocess.py
│ │ │ │ │ ├── temp_dir.py
│ │ │ │ │ ├── unpacking.py
│ │ │ │ │ ├── urls.py
│ │ │ │ │ ├── virtualenv.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── vcs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── bazaar.cpython-312.pyc
│ │ │ │ │ │ ├── git.cpython-312.pyc
│ │ │ │ │ │ ├── mercurial.cpython-312.pyc
│ │ │ │ │ │ ├── subversion.cpython-312.pyc
│ │ │ │ │ │ └── versioncontrol.cpython-312.pyc
│ │ │ │ │ ├── bazaar.py
│ │ │ │ │ ├── git.py
│ │ │ │ │ ├── mercurial.py
│ │ │ │ │ ├── subversion.py
│ │ │ │ │ └── versioncontrol.py
│ │ │ │ └── wheel_builder.py
│ │ │ ├── _vendor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ └── typing_extensions.cpython-312.pyc
│ │ │ │ ├── cachecontrol
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _cmd.cpython-312.pyc
│ │ │ │ │ │ ├── adapter.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── controller.cpython-312.pyc
│ │ │ │ │ │ ├── filewrapper.cpython-312.pyc
│ │ │ │ │ │ ├── heuristics.cpython-312.pyc
│ │ │ │ │ │ ├── serialize.cpython-312.pyc
│ │ │ │ │ │ └── wrapper.cpython-312.pyc
│ │ │ │ │ ├── _cmd.py
│ │ │ │ │ ├── adapter.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── caches
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── file_cache.cpython-312.pyc
│ │ │ │ │ │ │ └── redis_cache.cpython-312.pyc
│ │ │ │ │ │ ├── file_cache.py
│ │ │ │ │ │ └── redis_cache.py
│ │ │ │ │ ├── controller.py
│ │ │ │ │ ├── filewrapper.py
│ │ │ │ │ ├── heuristics.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── serialize.py
│ │ │ │ │ └── wrapper.py
│ │ │ │ ├── certifi
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── core.cpython-312.pyc
│ │ │ │ │ ├── cacert.pem
│ │ │ │ │ ├── core.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── distlib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── database.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── locators.cpython-312.pyc
│ │ │ │ │ │ ├── manifest.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── resources.cpython-312.pyc
│ │ │ │ │ │ ├── scripts.cpython-312.pyc
│ │ │ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── database.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── locators.py
│ │ │ │ │ ├── manifest.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ ├── scripts.py
│ │ │ │ │ ├── t32.exe
│ │ │ │ │ ├── t64-arm.exe
│ │ │ │ │ ├── t64.exe
│ │ │ │ │ ├── util.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ ├── w32.exe
│ │ │ │ │ ├── w64-arm.exe
│ │ │ │ │ ├── w64.exe
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── distro
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── distro.cpython-312.pyc
│ │ │ │ │ ├── distro.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── idna
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ │ │ ├── codec.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── core.py
│ │ │ │ │ ├── idnadata.py
│ │ │ │ │ ├── intranges.py
│ │ │ │ │ ├── package_data.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ └── uts46data.py
│ │ │ │ ├── msgpack
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ │ │ └── fallback.cpython-312.pyc
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── ext.py
│ │ │ │ │ └── fallback.py
│ │ │ │ ├── packaging
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _elffile.cpython-312.pyc
│ │ │ │ │ │ ├── _manylinux.cpython-312.pyc
│ │ │ │ │ │ ├── _musllinux.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _structures.cpython-312.pyc
│ │ │ │ │ │ ├── _tokenizer.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ ├── specifiers.cpython-312.pyc
│ │ │ │ │ │ ├── tags.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── version.cpython-312.pyc
│ │ │ │ │ ├── _elffile.py
│ │ │ │ │ ├── _manylinux.py
│ │ │ │ │ ├── _musllinux.py
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _structures.py
│ │ │ │ │ ├── _tokenizer.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ ├── specifiers.py
│ │ │ │ │ ├── tags.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── version.py
│ │ │ │ ├── pkg_resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── platformdirs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── android.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── macos.cpython-312.pyc
│ │ │ │ │ │ ├── unix.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── windows.cpython-312.pyc
│ │ │ │ │ ├── android.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── unix.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── pygments
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── cmdline.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── filter.cpython-312.pyc
│ │ │ │ │ │ ├── formatter.cpython-312.pyc
│ │ │ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ │ │ ├── modeline.cpython-312.pyc
│ │ │ │ │ │ ├── plugin.cpython-312.pyc
│ │ │ │ │ │ ├── regexopt.cpython-312.pyc
│ │ │ │ │ │ ├── scanner.cpython-312.pyc
│ │ │ │ │ │ ├── sphinxext.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── token.cpython-312.pyc
│ │ │ │ │ │ ├── unistring.cpython-312.pyc
│ │ │ │ │ │ └── util.cpython-312.pyc
│ │ │ │ │ ├── cmdline.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── filter.py
│ │ │ │ │ ├── filters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── formatter.py
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ ├── bbcode.cpython-312.pyc
│ │ │ │ │ │ │ ├── groff.cpython-312.pyc
│ │ │ │ │ │ │ ├── html.cpython-312.pyc
│ │ │ │ │ │ │ ├── img.cpython-312.pyc
│ │ │ │ │ │ │ ├── irc.cpython-312.pyc
│ │ │ │ │ │ │ ├── latex.cpython-312.pyc
│ │ │ │ │ │ │ ├── other.cpython-312.pyc
│ │ │ │ │ │ │ ├── pangomarkup.cpython-312.pyc
│ │ │ │ │ │ │ ├── rtf.cpython-312.pyc
│ │ │ │ │ │ │ ├── svg.cpython-312.pyc
│ │ │ │ │ │ │ ├── terminal.cpython-312.pyc
│ │ │ │ │ │ │ └── terminal256.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ ├── bbcode.py
│ │ │ │ │ │ ├── groff.py
│ │ │ │ │ │ ├── html.py
│ │ │ │ │ │ ├── img.py
│ │ │ │ │ │ ├── irc.py
│ │ │ │ │ │ ├── latex.py
│ │ │ │ │ │ ├── other.py
│ │ │ │ │ │ ├── pangomarkup.py
│ │ │ │ │ │ ├── rtf.py
│ │ │ │ │ │ ├── svg.py
│ │ │ │ │ │ ├── terminal.py
│ │ │ │ │ │ └── terminal256.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ ├── lexers
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ └── python.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ └── python.py
│ │ │ │ │ ├── modeline.py
│ │ │ │ │ ├── plugin.py
│ │ │ │ │ ├── regexopt.py
│ │ │ │ │ ├── scanner.py
│ │ │ │ │ ├── sphinxext.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styles
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── _mapping.cpython-312.pyc
│ │ │ │ │ │ └── _mapping.py
│ │ │ │ │ ├── token.py
│ │ │ │ │ ├── unistring.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── pyproject_hooks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ └── _impl.cpython-312.pyc
│ │ │ │ │ ├── _compat.py
│ │ │ │ │ ├── _impl.py
│ │ │ │ │ └── _in_process
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── _in_process.cpython-312.pyc
│ │ │ │ │ └── _in_process.py
│ │ │ │ ├── requests
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ │ ├── __version__.py
│ │ │ │ │ ├── _internal_utils.py
│ │ │ │ │ ├── adapters.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── certs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── cookies.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── hooks.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ ├── packages.py
│ │ │ │ │ ├── sessions.py
│ │ │ │ │ ├── status_codes.py
│ │ │ │ │ ├── structures.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── providers.cpython-312.pyc
│ │ │ │ │ │ ├── reporters.cpython-312.pyc
│ │ │ │ │ │ ├── resolvers.cpython-312.pyc
│ │ │ │ │ │ └── structs.cpython-312.pyc
│ │ │ │ │ ├── compat
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── collections_abc.cpython-312.pyc
│ │ │ │ │ │ └── collections_abc.py
│ │ │ │ │ ├── providers.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── reporters.py
│ │ │ │ │ ├── resolvers.py
│ │ │ │ │ └── structs.py
│ │ │ │ ├── rich
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── _cell_widths.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_codes.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_replace.cpython-312.pyc
│ │ │ │ │ │ ├── _export_format.cpython-312.pyc
│ │ │ │ │ │ ├── _extension.cpython-312.pyc
│ │ │ │ │ │ ├── _fileno.cpython-312.pyc
│ │ │ │ │ │ ├── _inspect.cpython-312.pyc
│ │ │ │ │ │ ├── _log_render.cpython-312.pyc
│ │ │ │ │ │ ├── _loop.cpython-312.pyc
│ │ │ │ │ │ ├── _null_file.cpython-312.pyc
│ │ │ │ │ │ ├── _palettes.cpython-312.pyc
│ │ │ │ │ │ ├── _pick.cpython-312.pyc
│ │ │ │ │ │ ├── _ratio.cpython-312.pyc
│ │ │ │ │ │ ├── _spinners.cpython-312.pyc
│ │ │ │ │ │ ├── _stack.cpython-312.pyc
│ │ │ │ │ │ ├── _timer.cpython-312.pyc
│ │ │ │ │ │ ├── _win32_console.cpython-312.pyc
│ │ │ │ │ │ ├── _windows_renderer.cpython-312.pyc
│ │ │ │ │ │ ├── _windows.cpython-312.pyc
│ │ │ │ │ │ ├── _wrap.cpython-312.pyc
│ │ │ │ │ │ ├── abc.cpython-312.pyc
│ │ │ │ │ │ ├── align.cpython-312.pyc
│ │ │ │ │ │ ├── ansi.cpython-312.pyc
│ │ │ │ │ │ ├── bar.cpython-312.pyc
│ │ │ │ │ │ ├── box.cpython-312.pyc
│ │ │ │ │ │ ├── cells.cpython-312.pyc
│ │ │ │ │ │ ├── color_triplet.cpython-312.pyc
│ │ │ │ │ │ ├── color.cpython-312.pyc
│ │ │ │ │ │ ├── columns.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── constrain.cpython-312.pyc
│ │ │ │ │ │ ├── containers.cpython-312.pyc
│ │ │ │ │ │ ├── control.cpython-312.pyc
│ │ │ │ │ │ ├── default_styles.cpython-312.pyc
│ │ │ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ │ │ ├── emoji.cpython-312.pyc
│ │ │ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ │ │ ├── file_proxy.cpython-312.pyc
│ │ │ │ │ │ ├── filesize.cpython-312.pyc
│ │ │ │ │ │ ├── highlighter.cpython-312.pyc
│ │ │ │ │ │ ├── json.cpython-312.pyc
│ │ │ │ │ │ ├── jupyter.cpython-312.pyc
│ │ │ │ │ │ ├── layout.cpython-312.pyc
│ │ │ │ │ │ ├── live_render.cpython-312.pyc
│ │ │ │ │ │ ├── live.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── markup.cpython-312.pyc
│ │ │ │ │ │ ├── measure.cpython-312.pyc
│ │ │ │ │ │ ├── padding.cpython-312.pyc
│ │ │ │ │ │ ├── pager.cpython-312.pyc
│ │ │ │ │ │ ├── palette.cpython-312.pyc
│ │ │ │ │ │ ├── panel.cpython-312.pyc
│ │ │ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bar.cpython-312.pyc
│ │ │ │ │ │ ├── progress.cpython-312.pyc
│ │ │ │ │ │ ├── prompt.cpython-312.pyc
│ │ │ │ │ │ ├── protocol.cpython-312.pyc
│ │ │ │ │ │ ├── region.cpython-312.pyc
│ │ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ │ ├── rule.cpython-312.pyc
│ │ │ │ │ │ ├── scope.cpython-312.pyc
│ │ │ │ │ │ ├── screen.cpython-312.pyc
│ │ │ │ │ │ ├── segment.cpython-312.pyc
│ │ │ │ │ │ ├── spinner.cpython-312.pyc
│ │ │ │ │ │ ├── status.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── styled.cpython-312.pyc
│ │ │ │ │ │ ├── syntax.cpython-312.pyc
│ │ │ │ │ │ ├── table.cpython-312.pyc
│ │ │ │ │ │ ├── terminal_theme.cpython-312.pyc
│ │ │ │ │ │ ├── text.cpython-312.pyc
│ │ │ │ │ │ ├── theme.cpython-312.pyc
│ │ │ │ │ │ ├── themes.cpython-312.pyc
│ │ │ │ │ │ ├── traceback.cpython-312.pyc
│ │ │ │ │ │ └── tree.cpython-312.pyc
│ │ │ │ │ ├── _cell_widths.py
│ │ │ │ │ ├── _emoji_codes.py
│ │ │ │ │ ├── _emoji_replace.py
│ │ │ │ │ ├── _export_format.py
│ │ │ │ │ ├── _extension.py
│ │ │ │ │ ├── _fileno.py
│ │ │ │ │ ├── _inspect.py
│ │ │ │ │ ├── _log_render.py
│ │ │ │ │ ├── _loop.py
│ │ │ │ │ ├── _null_file.py
│ │ │ │ │ ├── _palettes.py
│ │ │ │ │ ├── _pick.py
│ │ │ │ │ ├── _ratio.py
│ │ │ │ │ ├── _spinners.py
│ │ │ │ │ ├── _stack.py
│ │ │ │ │ ├── _timer.py
│ │ │ │ │ ├── _win32_console.py
│ │ │ │ │ ├── _windows_renderer.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ ├── _wrap.py
│ │ │ │ │ ├── abc.py
│ │ │ │ │ ├── align.py
│ │ │ │ │ ├── ansi.py
│ │ │ │ │ ├── bar.py
│ │ │ │ │ ├── box.py
│ │ │ │ │ ├── cells.py
│ │ │ │ │ ├── color_triplet.py
│ │ │ │ │ ├── color.py
│ │ │ │ │ ├── columns.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── constrain.py
│ │ │ │ │ ├── containers.py
│ │ │ │ │ ├── control.py
│ │ │ │ │ ├── default_styles.py
│ │ │ │ │ ├── diagnose.py
│ │ │ │ │ ├── emoji.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_proxy.py
│ │ │ │ │ ├── filesize.py
│ │ │ │ │ ├── highlighter.py
│ │ │ │ │ ├── json.py
│ │ │ │ │ ├── jupyter.py
│ │ │ │ │ ├── layout.py
│ │ │ │ │ ├── live_render.py
│ │ │ │ │ ├── live.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── markup.py
│ │ │ │ │ ├── measure.py
│ │ │ │ │ ├── padding.py
│ │ │ │ │ ├── pager.py
│ │ │ │ │ ├── palette.py
│ │ │ │ │ ├── panel.py
│ │ │ │ │ ├── pretty.py
│ │ │ │ │ ├── progress_bar.py
│ │ │ │ │ ├── progress.py
│ │ │ │ │ ├── prompt.py
│ │ │ │ │ ├── protocol.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── region.py
│ │ │ │ │ ├── repr.py
│ │ │ │ │ ├── rule.py
│ │ │ │ │ ├── scope.py
│ │ │ │ │ ├── screen.py
│ │ │ │ │ ├── segment.py
│ │ │ │ │ ├── spinner.py
│ │ │ │ │ ├── status.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styled.py
│ │ │ │ │ ├── syntax.py
│ │ │ │ │ ├── table.py
│ │ │ │ │ ├── terminal_theme.py
│ │ │ │ │ ├── text.py
│ │ │ │ │ ├── theme.py
│ │ │ │ │ ├── themes.py
│ │ │ │ │ ├── traceback.py
│ │ │ │ │ └── tree.py
│ │ │ │ ├── tomli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _re.cpython-312.pyc
│ │ │ │ │ │ └── _types.cpython-312.pyc
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _re.py
│ │ │ │ │ ├── _types.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── truststore
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _api.cpython-312.pyc
│ │ │ │ │ │ ├── _macos.cpython-312.pyc
│ │ │ │ │ │ ├── _openssl.cpython-312.pyc
│ │ │ │ │ │ ├── _ssl_constants.cpython-312.pyc
│ │ │ │ │ │ └── _windows.cpython-312.pyc
│ │ │ │ │ ├── _api.py
│ │ │ │ │ ├── _macos.py
│ │ │ │ │ ├── _openssl.py
│ │ │ │ │ ├── _ssl_constants.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── typing_extensions.py
│ │ │ │ ├── urllib3
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── _collections.py
│ │ │ │ │ ├── _version.py
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── connectionpool.py
│ │ │ │ │ ├── contrib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _appengine_environ.cpython-312.pyc
│ │ │ │ │ │ │ ├── appengine.cpython-312.pyc
│ │ │ │ │ │ │ ├── ntlmpool.cpython-312.pyc
│ │ │ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ │ │ ├── securetransport.cpython-312.pyc
│ │ │ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ │ │ ├── _appengine_environ.py
│ │ │ │ │ │ ├── _securetransport
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── bindings.cpython-312.pyc
│ │ │ │ │ │ │ │ └── low_level.cpython-312.pyc
│ │ │ │ │ │ │ ├── bindings.py
│ │ │ │ │ │ │ └── low_level.py
│ │ │ │ │ │ ├── appengine.py
│ │ │ │ │ │ ├── ntlmpool.py
│ │ │ │ │ │ ├── pyopenssl.py
│ │ │ │ │ │ ├── securetransport.py
│ │ │ │ │ │ └── socks.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── fields.py
│ │ │ │ │ ├── filepost.py
│ │ │ │ │ ├── packages
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── six.cpython-312.pyc
│ │ │ │ │ │ ├── backports
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── makefile.cpython-312.pyc
│ │ │ │ │ │ │ │ └── weakref_finalize.cpython-312.pyc
│ │ │ │ │ │ │ ├── makefile.py
│ │ │ │ │ │ │ └── weakref_finalize.py
│ │ │ │ │ │ └── six.py
│ │ │ │ │ ├── poolmanager.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ └── util
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ │ │ ├── queue.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ │ │ └── wait.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── proxy.py
│ │ │ │ │ ├── queue.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── ssl_.py
│ │ │ │ │ ├── ssl_match_hostname.py
│ │ │ │ │ ├── ssltransport.py
│ │ │ │ │ ├── timeout.py
│ │ │ │ │ ├── url.py
│ │ │ │ │ └── wait.py
│ │ │ │ └── vendor.txt
│ │ │ └── py.typed
│ │ ├── pip-24.2.dist-info
│ │ │ ├── AUTHORS.txt
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── requests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── __version__.py
│ │ │ ├── _internal_utils.py
│ │ │ ├── adapters.py
│ │ │ ├── api.py
│ │ │ ├── auth.py
│ │ │ ├── certs.py
│ │ │ ├── compat.py
│ │ │ ├── cookies.py
│ │ │ ├── exceptions.py
│ │ │ ├── help.py
│ │ │ ├── hooks.py
│ │ │ ├── models.py
│ │ │ ├── packages.py
│ │ │ ├── sessions.py
│ │ │ ├── status_codes.py
│ │ │ ├── structures.py
│ │ │ └── utils.py
│ │ ├── requests-2.32.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── soupsieve
│ │ │ ├── __init__.py
│ │ │ ├── __meta__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __meta__.cpython-312.pyc
│ │ │ │ ├── css_match.cpython-312.pyc
│ │ │ │ ├── css_parser.cpython-312.pyc
│ │ │ │ ├── css_types.cpython-312.pyc
│ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ └── util.cpython-312.pyc
│ │ │ ├── css_match.py
│ │ │ ├── css_parser.py
│ │ │ ├── css_types.py
│ │ │ ├── pretty.py
│ │ │ ├── py.typed
│ │ │ └── util.py
│ │ ├── soupsieve-2.6.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── urllib3
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _base_connection.cpython-312.pyc
│ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ ├── _request_methods.cpython-312.pyc
│ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ └── response.cpython-312.pyc
│ │ │ ├── _base_connection.py
│ │ │ ├── _collections.py
│ │ │ ├── _request_methods.py
│ │ │ ├── _version.py
│ │ │ ├── connection.py
│ │ │ ├── connectionpool.py
│ │ │ ├── contrib
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ ├── emscripten
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── fetch.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── emscripten_fetch_worker.js
│ │ │ │ │ ├── fetch.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ └── response.py
│ │ │ │ ├── pyopenssl.py
│ │ │ │ └── socks.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── filepost.py
│ │ │ ├── http2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ └── probe.cpython-312.pyc
│ │ │ │ ├── connection.py
│ │ │ │ └── probe.py
│ │ │ ├── poolmanager.py
│ │ │ ├── py.typed
│ │ │ ├── response.py
│ │ │ └── util
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ └── wait.cpython-312.pyc
│ │ │ ├── connection.py
│ │ │ ├── proxy.py
│ │ │ ├── request.py
│ │ │ ├── response.py
│ │ │ ├── retry.py
│ │ │ ├── ssl_.py
│ │ │ ├── ssl_match_hostname.py
│ │ │ ├── ssltransport.py
│ │ │ ├── timeout.py
│ │ │ ├── url.py
│ │ │ ├── util.py
│ │ │ └── wait.py
│ │ ├── urllib3-2.2.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── useragent
│ │ │ ├── __init__.py
│ │ │ ├── __init__.pyc
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── resources
│ │ │ │ └── user_agent_data.json
│ │ │ └── test
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── test_additional_os.json
│ │ │ ├── test_browser.json
│ │ │ ├── test_device.json
│ │ │ ├── test_firefox.json
│ │ │ ├── test_os.json
│ │ │ └── test_pgts_browser.json
│ │ ├── useragent-0.1.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── werkzeug
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _internal.cpython-312.pyc
│ │ │ │ ├── _reloader.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formparser.cpython-312.pyc
│ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ ├── local.cpython-312.pyc
│ │ │ │ ├── security.cpython-312.pyc
│ │ │ │ ├── serving.cpython-312.pyc
│ │ │ │ ├── test.cpython-312.pyc
│ │ │ │ ├── testapp.cpython-312.pyc
│ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ ├── user_agent.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── wsgi.cpython-312.pyc
│ │ │ ├── _internal.py
│ │ │ ├── _reloader.py
│ │ │ ├── datastructures
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── accept.cpython-312.pyc
│ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ ├── cache_control.cpython-312.pyc
│ │ │ │ │ ├── csp.cpython-312.pyc
│ │ │ │ │ ├── etag.cpython-312.pyc
│ │ │ │ │ ├── file_storage.cpython-312.pyc
│ │ │ │ │ ├── headers.cpython-312.pyc
│ │ │ │ │ ├── mixins.cpython-312.pyc
│ │ │ │ │ ├── range.cpython-312.pyc
│ │ │ │ │ └── structures.cpython-312.pyc
│ │ │ │ ├── accept.py
│ │ │ │ ├── accept.pyi
│ │ │ │ ├── auth.py
│ │ │ │ ├── cache_control.py
│ │ │ │ ├── cache_control.pyi
│ │ │ │ ├── csp.py
│ │ │ │ ├── csp.pyi
│ │ │ │ ├── etag.py
│ │ │ │ ├── etag.pyi
│ │ │ │ ├── file_storage.py
│ │ │ │ ├── file_storage.pyi
│ │ │ │ ├── headers.py
│ │ │ │ ├── headers.pyi
│ │ │ │ ├── mixins.py
│ │ │ │ ├── mixins.pyi
│ │ │ │ ├── range.py
│ │ │ │ ├── range.pyi
│ │ │ │ ├── structures.py
│ │ │ │ └── structures.pyi
│ │ │ ├── debug
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ └── tbtools.cpython-312.pyc
│ │ │ │ ├── console.py
│ │ │ │ ├── repr.py
│ │ │ │ ├── shared
│ │ │ │ │ ├── console.png
│ │ │ │ │ ├── debugger.js
│ │ │ │ │ ├── ICON_LICENSE.md
│ │ │ │ │ ├── less.png
│ │ │ │ │ ├── more.png
│ │ │ │ │ └── style.css
│ │ │ │ └── tbtools.py
│ │ │ ├── exceptions.py
│ │ │ ├── formparser.py
│ │ │ ├── http.py
│ │ │ ├── local.py
│ │ │ ├── middleware
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── dispatcher.cpython-312.pyc
│ │ │ │ │ ├── http_proxy.cpython-312.pyc
│ │ │ │ │ ├── lint.cpython-312.pyc
│ │ │ │ │ ├── profiler.cpython-312.pyc
│ │ │ │ │ ├── proxy_fix.cpython-312.pyc
│ │ │ │ │ └── shared_data.cpython-312.pyc
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── http_proxy.py
│ │ │ │ ├── lint.py
│ │ │ │ ├── profiler.py
│ │ │ │ ├── proxy_fix.py
│ │ │ │ └── shared_data.py
│ │ │ ├── py.typed
│ │ │ ├── routing
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── converters.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── map.cpython-312.pyc
│ │ │ │ │ ├── matcher.cpython-312.pyc
│ │ │ │ │ └── rules.cpython-312.pyc
│ │ │ │ ├── converters.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── map.py
│ │ │ │ ├── matcher.py
│ │ │ │ └── rules.py
│ │ │ ├── sansio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ │ ├── multipart.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ ├── http.py
│ │ │ │ ├── multipart.py
│ │ │ │ ├── request.py
│ │ │ │ ├── response.py
│ │ │ │ └── utils.py
│ │ │ ├── security.py
│ │ │ ├── serving.py
│ │ │ ├── test.py
│ │ │ ├── testapp.py
│ │ │ ├── urls.py
│ │ │ ├── user_agent.py
│ │ │ ├── utils.py
│ │ │ ├── wrappers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ ├── request.py
│ │ │ │ └── response.py
│ │ │ └── wsgi.py
│ │ └── werkzeug-3.0.4.dist-info
│ │ ├── INSTALLER
│ │ ├── LICENSE.txt
│ │ ├── METADATA
│ │ ├── RECORD
│ │ └── WHEEL
│ ├── pyvenv.cfg
│ ├── static
│ │ └── styles.css
│ ├── templates
│ │ └── index.html
│ └── test.py
├── cline_config.json
├── mcp_server.py
├── README.md
├── search_results.json
├── settings.json
└── test_files
├── text1.txt
└── text2.txt
```
# Files
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/response.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import absolute_import
2 |
3 | import io
4 | import logging
5 | import sys
6 | import warnings
7 | import zlib
8 | from contextlib import contextmanager
9 | from socket import error as SocketError
10 | from socket import timeout as SocketTimeout
11 |
12 | brotli = None
13 |
14 | from . import util
15 | from ._collections import HTTPHeaderDict
16 | from .connection import BaseSSLError, HTTPException
17 | from .exceptions import (
18 | BodyNotHttplibCompatible,
19 | DecodeError,
20 | HTTPError,
21 | IncompleteRead,
22 | InvalidChunkLength,
23 | InvalidHeader,
24 | ProtocolError,
25 | ReadTimeoutError,
26 | ResponseNotChunked,
27 | SSLError,
28 | )
29 | from .packages import six
30 | from .util.response import is_fp_closed, is_response_to_head
31 |
32 | log = logging.getLogger(__name__)
33 |
34 |
35 | class DeflateDecoder(object):
36 | def __init__(self):
37 | self._first_try = True
38 | self._data = b""
39 | self._obj = zlib.decompressobj()
40 |
41 | def __getattr__(self, name):
42 | return getattr(self._obj, name)
43 |
44 | def decompress(self, data):
45 | if not data:
46 | return data
47 |
48 | if not self._first_try:
49 | return self._obj.decompress(data)
50 |
51 | self._data += data
52 | try:
53 | decompressed = self._obj.decompress(data)
54 | if decompressed:
55 | self._first_try = False
56 | self._data = None
57 | return decompressed
58 | except zlib.error:
59 | self._first_try = False
60 | self._obj = zlib.decompressobj(-zlib.MAX_WBITS)
61 | try:
62 | return self.decompress(self._data)
63 | finally:
64 | self._data = None
65 |
66 |
67 | class GzipDecoderState(object):
68 |
69 | FIRST_MEMBER = 0
70 | OTHER_MEMBERS = 1
71 | SWALLOW_DATA = 2
72 |
73 |
74 | class GzipDecoder(object):
75 | def __init__(self):
76 | self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
77 | self._state = GzipDecoderState.FIRST_MEMBER
78 |
79 | def __getattr__(self, name):
80 | return getattr(self._obj, name)
81 |
82 | def decompress(self, data):
83 | ret = bytearray()
84 | if self._state == GzipDecoderState.SWALLOW_DATA or not data:
85 | return bytes(ret)
86 | while True:
87 | try:
88 | ret += self._obj.decompress(data)
89 | except zlib.error:
90 | previous_state = self._state
91 | # Ignore data after the first error
92 | self._state = GzipDecoderState.SWALLOW_DATA
93 | if previous_state == GzipDecoderState.OTHER_MEMBERS:
94 | # Allow trailing garbage acceptable in other gzip clients
95 | return bytes(ret)
96 | raise
97 | data = self._obj.unused_data
98 | if not data:
99 | return bytes(ret)
100 | self._state = GzipDecoderState.OTHER_MEMBERS
101 | self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
102 |
103 |
104 | if brotli is not None:
105 |
106 | class BrotliDecoder(object):
107 | # Supports both 'brotlipy' and 'Brotli' packages
108 | # since they share an import name. The top branches
109 | # are for 'brotlipy' and bottom branches for 'Brotli'
110 | def __init__(self):
111 | self._obj = brotli.Decompressor()
112 | if hasattr(self._obj, "decompress"):
113 | self.decompress = self._obj.decompress
114 | else:
115 | self.decompress = self._obj.process
116 |
117 | def flush(self):
118 | if hasattr(self._obj, "flush"):
119 | return self._obj.flush()
120 | return b""
121 |
122 |
123 | class MultiDecoder(object):
124 | """
125 | From RFC7231:
126 | If one or more encodings have been applied to a representation, the
127 | sender that applied the encodings MUST generate a Content-Encoding
128 | header field that lists the content codings in the order in which
129 | they were applied.
130 | """
131 |
132 | def __init__(self, modes):
133 | self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")]
134 |
135 | def flush(self):
136 | return self._decoders[0].flush()
137 |
138 | def decompress(self, data):
139 | for d in reversed(self._decoders):
140 | data = d.decompress(data)
141 | return data
142 |
143 |
144 | def _get_decoder(mode):
145 | if "," in mode:
146 | return MultiDecoder(mode)
147 |
148 | if mode == "gzip":
149 | return GzipDecoder()
150 |
151 | if brotli is not None and mode == "br":
152 | return BrotliDecoder()
153 |
154 | return DeflateDecoder()
155 |
156 |
157 | class HTTPResponse(io.IOBase):
158 | """
159 | HTTP Response container.
160 |
161 | Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is
162 | loaded and decoded on-demand when the ``data`` property is accessed. This
163 | class is also compatible with the Python standard library's :mod:`io`
164 | module, and can hence be treated as a readable object in the context of that
165 | framework.
166 |
167 | Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`:
168 |
169 | :param preload_content:
170 | If True, the response's body will be preloaded during construction.
171 |
172 | :param decode_content:
173 | If True, will attempt to decode the body based on the
174 | 'content-encoding' header.
175 |
176 | :param original_response:
177 | When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse`
178 | object, it's convenient to include the original for debug purposes. It's
179 | otherwise unused.
180 |
181 | :param retries:
182 | The retries contains the last :class:`~urllib3.util.retry.Retry` that
183 | was used during the request.
184 |
185 | :param enforce_content_length:
186 | Enforce content length checking. Body returned by server must match
187 | value of Content-Length header, if present. Otherwise, raise error.
188 | """
189 |
190 | CONTENT_DECODERS = ["gzip", "deflate"]
191 | if brotli is not None:
192 | CONTENT_DECODERS += ["br"]
193 | REDIRECT_STATUSES = [301, 302, 303, 307, 308]
194 |
195 | def __init__(
196 | self,
197 | body="",
198 | headers=None,
199 | status=0,
200 | version=0,
201 | reason=None,
202 | strict=0,
203 | preload_content=True,
204 | decode_content=True,
205 | original_response=None,
206 | pool=None,
207 | connection=None,
208 | msg=None,
209 | retries=None,
210 | enforce_content_length=False,
211 | request_method=None,
212 | request_url=None,
213 | auto_close=True,
214 | ):
215 |
216 | if isinstance(headers, HTTPHeaderDict):
217 | self.headers = headers
218 | else:
219 | self.headers = HTTPHeaderDict(headers)
220 | self.status = status
221 | self.version = version
222 | self.reason = reason
223 | self.strict = strict
224 | self.decode_content = decode_content
225 | self.retries = retries
226 | self.enforce_content_length = enforce_content_length
227 | self.auto_close = auto_close
228 |
229 | self._decoder = None
230 | self._body = None
231 | self._fp = None
232 | self._original_response = original_response
233 | self._fp_bytes_read = 0
234 | self.msg = msg
235 | self._request_url = request_url
236 |
237 | if body and isinstance(body, (six.string_types, bytes)):
238 | self._body = body
239 |
240 | self._pool = pool
241 | self._connection = connection
242 |
243 | if hasattr(body, "read"):
244 | self._fp = body
245 |
246 | # Are we using the chunked-style of transfer encoding?
247 | self.chunked = False
248 | self.chunk_left = None
249 | tr_enc = self.headers.get("transfer-encoding", "").lower()
250 | # Don't incur the penalty of creating a list and then discarding it
251 | encodings = (enc.strip() for enc in tr_enc.split(","))
252 | if "chunked" in encodings:
253 | self.chunked = True
254 |
255 | # Determine length of response
256 | self.length_remaining = self._init_length(request_method)
257 |
258 | # If requested, preload the body.
259 | if preload_content and not self._body:
260 | self._body = self.read(decode_content=decode_content)
261 |
262 | def get_redirect_location(self):
263 | """
264 | Should we redirect and where to?
265 |
266 | :returns: Truthy redirect location string if we got a redirect status
267 | code and valid location. ``None`` if redirect status and no
268 | location. ``False`` if not a redirect status code.
269 | """
270 | if self.status in self.REDIRECT_STATUSES:
271 | return self.headers.get("location")
272 |
273 | return False
274 |
275 | def release_conn(self):
276 | if not self._pool or not self._connection:
277 | return
278 |
279 | self._pool._put_conn(self._connection)
280 | self._connection = None
281 |
282 | def drain_conn(self):
283 | """
284 | Read and discard any remaining HTTP response data in the response connection.
285 |
286 | Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
287 | """
288 | try:
289 | self.read()
290 | except (HTTPError, SocketError, BaseSSLError, HTTPException):
291 | pass
292 |
293 | @property
294 | def data(self):
295 | # For backwards-compat with earlier urllib3 0.4 and earlier.
296 | if self._body:
297 | return self._body
298 |
299 | if self._fp:
300 | return self.read(cache_content=True)
301 |
302 | @property
303 | def connection(self):
304 | return self._connection
305 |
306 | def isclosed(self):
307 | return is_fp_closed(self._fp)
308 |
309 | def tell(self):
310 | """
311 | Obtain the number of bytes pulled over the wire so far. May differ from
312 | the amount of content returned by :meth:``urllib3.response.HTTPResponse.read``
313 | if bytes are encoded on the wire (e.g, compressed).
314 | """
315 | return self._fp_bytes_read
316 |
317 | def _init_length(self, request_method):
318 | """
319 | Set initial length value for Response content if available.
320 | """
321 | length = self.headers.get("content-length")
322 |
323 | if length is not None:
324 | if self.chunked:
325 | # This Response will fail with an IncompleteRead if it can't be
326 | # received as chunked. This method falls back to attempt reading
327 | # the response before raising an exception.
328 | log.warning(
329 | "Received response with both Content-Length and "
330 | "Transfer-Encoding set. This is expressly forbidden "
331 | "by RFC 7230 sec 3.3.2. Ignoring Content-Length and "
332 | "attempting to process response as Transfer-Encoding: "
333 | "chunked."
334 | )
335 | return None
336 |
337 | try:
338 | # RFC 7230 section 3.3.2 specifies multiple content lengths can
339 | # be sent in a single Content-Length header
340 | # (e.g. Content-Length: 42, 42). This line ensures the values
341 | # are all valid ints and that as long as the `set` length is 1,
342 | # all values are the same. Otherwise, the header is invalid.
343 | lengths = set([int(val) for val in length.split(",")])
344 | if len(lengths) > 1:
345 | raise InvalidHeader(
346 | "Content-Length contained multiple "
347 | "unmatching values (%s)" % length
348 | )
349 | length = lengths.pop()
350 | except ValueError:
351 | length = None
352 | else:
353 | if length < 0:
354 | length = None
355 |
356 | # Convert status to int for comparison
357 | # In some cases, httplib returns a status of "_UNKNOWN"
358 | try:
359 | status = int(self.status)
360 | except ValueError:
361 | status = 0
362 |
363 | # Check for responses that shouldn't include a body
364 | if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD":
365 | length = 0
366 |
367 | return length
368 |
369 | def _init_decoder(self):
370 | """
371 | Set-up the _decoder attribute if necessary.
372 | """
373 | # Note: content-encoding value should be case-insensitive, per RFC 7230
374 | # Section 3.2
375 | content_encoding = self.headers.get("content-encoding", "").lower()
376 | if self._decoder is None:
377 | if content_encoding in self.CONTENT_DECODERS:
378 | self._decoder = _get_decoder(content_encoding)
379 | elif "," in content_encoding:
380 | encodings = [
381 | e.strip()
382 | for e in content_encoding.split(",")
383 | if e.strip() in self.CONTENT_DECODERS
384 | ]
385 | if len(encodings):
386 | self._decoder = _get_decoder(content_encoding)
387 |
388 | DECODER_ERROR_CLASSES = (IOError, zlib.error)
389 | if brotli is not None:
390 | DECODER_ERROR_CLASSES += (brotli.error,)
391 |
392 | def _decode(self, data, decode_content, flush_decoder):
393 | """
394 | Decode the data passed in and potentially flush the decoder.
395 | """
396 | if not decode_content:
397 | return data
398 |
399 | try:
400 | if self._decoder:
401 | data = self._decoder.decompress(data)
402 | except self.DECODER_ERROR_CLASSES as e:
403 | content_encoding = self.headers.get("content-encoding", "").lower()
404 | raise DecodeError(
405 | "Received response with content-encoding: %s, but "
406 | "failed to decode it." % content_encoding,
407 | e,
408 | )
409 | if flush_decoder:
410 | data += self._flush_decoder()
411 |
412 | return data
413 |
414 | def _flush_decoder(self):
415 | """
416 | Flushes the decoder. Should only be called if the decoder is actually
417 | being used.
418 | """
419 | if self._decoder:
420 | buf = self._decoder.decompress(b"")
421 | return buf + self._decoder.flush()
422 |
423 | return b""
424 |
425 | @contextmanager
426 | def _error_catcher(self):
427 | """
428 | Catch low-level python exceptions, instead re-raising urllib3
429 | variants, so that low-level exceptions are not leaked in the
430 | high-level api.
431 |
432 | On exit, release the connection back to the pool.
433 | """
434 | clean_exit = False
435 |
436 | try:
437 | try:
438 | yield
439 |
440 | except SocketTimeout:
441 | # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but
442 | # there is yet no clean way to get at it from this context.
443 | raise ReadTimeoutError(self._pool, None, "Read timed out.")
444 |
445 | except BaseSSLError as e:
446 | # FIXME: Is there a better way to differentiate between SSLErrors?
447 | if "read operation timed out" not in str(e):
448 | # SSL errors related to framing/MAC get wrapped and reraised here
449 | raise SSLError(e)
450 |
451 | raise ReadTimeoutError(self._pool, None, "Read timed out.")
452 |
453 | except (HTTPException, SocketError) as e:
454 | # This includes IncompleteRead.
455 | raise ProtocolError("Connection broken: %r" % e, e)
456 |
457 | # If no exception is thrown, we should avoid cleaning up
458 | # unnecessarily.
459 | clean_exit = True
460 | finally:
461 | # If we didn't terminate cleanly, we need to throw away our
462 | # connection.
463 | if not clean_exit:
464 | # The response may not be closed but we're not going to use it
465 | # anymore so close it now to ensure that the connection is
466 | # released back to the pool.
467 | if self._original_response:
468 | self._original_response.close()
469 |
470 | # Closing the response may not actually be sufficient to close
471 | # everything, so if we have a hold of the connection close that
472 | # too.
473 | if self._connection:
474 | self._connection.close()
475 |
476 | # If we hold the original response but it's closed now, we should
477 | # return the connection back to the pool.
478 | if self._original_response and self._original_response.isclosed():
479 | self.release_conn()
480 |
481 | def _fp_read(self, amt):
482 | """
483 | Read a response with the thought that reading the number of bytes
484 | larger than can fit in a 32-bit int at a time via SSL in some
485 | known cases leads to an overflow error that has to be prevented
486 | if `amt` or `self.length_remaining` indicate that a problem may
487 | happen.
488 |
489 | The known cases:
490 | * 3.8 <= CPython < 3.9.7 because of a bug
491 | https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900.
492 | * urllib3 injected with pyOpenSSL-backed SSL-support.
493 | * CPython < 3.10 only when `amt` does not fit 32-bit int.
494 | """
495 | assert self._fp
496 | c_int_max = 2 ** 31 - 1
497 | if (
498 | (
499 | (amt and amt > c_int_max)
500 | or (self.length_remaining and self.length_remaining > c_int_max)
501 | )
502 | and not util.IS_SECURETRANSPORT
503 | and (util.IS_PYOPENSSL or sys.version_info < (3, 10))
504 | ):
505 | buffer = io.BytesIO()
506 | # Besides `max_chunk_amt` being a maximum chunk size, it
507 | # affects memory overhead of reading a response by this
508 | # method in CPython.
509 | # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum
510 | # chunk size that does not lead to an overflow error, but
511 | # 256 MiB is a compromise.
512 | max_chunk_amt = 2 ** 28
513 | while amt is None or amt != 0:
514 | if amt is not None:
515 | chunk_amt = min(amt, max_chunk_amt)
516 | amt -= chunk_amt
517 | else:
518 | chunk_amt = max_chunk_amt
519 | data = self._fp.read(chunk_amt)
520 | if not data:
521 | break
522 | buffer.write(data)
523 | del data # to reduce peak memory usage by `max_chunk_amt`.
524 | return buffer.getvalue()
525 | else:
526 | # StringIO doesn't like amt=None
527 | return self._fp.read(amt) if amt is not None else self._fp.read()
528 |
529 | def read(self, amt=None, decode_content=None, cache_content=False):
530 | """
531 | Similar to :meth:`http.client.HTTPResponse.read`, but with two additional
532 | parameters: ``decode_content`` and ``cache_content``.
533 |
534 | :param amt:
535 | How much of the content to read. If specified, caching is skipped
536 | because it doesn't make sense to cache partial content as the full
537 | response.
538 |
539 | :param decode_content:
540 | If True, will attempt to decode the body based on the
541 | 'content-encoding' header.
542 |
543 | :param cache_content:
544 | If True, will save the returned data such that the same result is
545 | returned despite of the state of the underlying file object. This
546 | is useful if you want the ``.data`` property to continue working
547 | after having ``.read()`` the file object. (Overridden if ``amt`` is
548 | set.)
549 | """
550 | self._init_decoder()
551 | if decode_content is None:
552 | decode_content = self.decode_content
553 |
554 | if self._fp is None:
555 | return
556 |
557 | flush_decoder = False
558 | fp_closed = getattr(self._fp, "closed", False)
559 |
560 | with self._error_catcher():
561 | data = self._fp_read(amt) if not fp_closed else b""
562 | if amt is None:
563 | flush_decoder = True
564 | else:
565 | cache_content = False
566 | if (
567 | amt != 0 and not data
568 | ): # Platform-specific: Buggy versions of Python.
569 | # Close the connection when no data is returned
570 | #
571 | # This is redundant to what httplib/http.client _should_
572 | # already do. However, versions of python released before
573 | # December 15, 2012 (http://bugs.python.org/issue16298) do
574 | # not properly close the connection in all cases. There is
575 | # no harm in redundantly calling close.
576 | self._fp.close()
577 | flush_decoder = True
578 | if self.enforce_content_length and self.length_remaining not in (
579 | 0,
580 | None,
581 | ):
582 | # This is an edge case that httplib failed to cover due
583 | # to concerns of backward compatibility. We're
584 | # addressing it here to make sure IncompleteRead is
585 | # raised during streaming, so all calls with incorrect
586 | # Content-Length are caught.
587 | raise IncompleteRead(self._fp_bytes_read, self.length_remaining)
588 |
589 | if data:
590 | self._fp_bytes_read += len(data)
591 | if self.length_remaining is not None:
592 | self.length_remaining -= len(data)
593 |
594 | data = self._decode(data, decode_content, flush_decoder)
595 |
596 | if cache_content:
597 | self._body = data
598 |
599 | return data
600 |
601 | def stream(self, amt=2 ** 16, decode_content=None):
602 | """
603 | A generator wrapper for the read() method. A call will block until
604 | ``amt`` bytes have been read from the connection or until the
605 | connection is closed.
606 |
607 | :param amt:
608 | How much of the content to read. The generator will return up to
609 | much data per iteration, but may return less. This is particularly
610 | likely when using compressed data. However, the empty string will
611 | never be returned.
612 |
613 | :param decode_content:
614 | If True, will attempt to decode the body based on the
615 | 'content-encoding' header.
616 | """
617 | if self.chunked and self.supports_chunked_reads():
618 | for line in self.read_chunked(amt, decode_content=decode_content):
619 | yield line
620 | else:
621 | while not is_fp_closed(self._fp):
622 | data = self.read(amt=amt, decode_content=decode_content)
623 |
624 | if data:
625 | yield data
626 |
627 | @classmethod
628 | def from_httplib(ResponseCls, r, **response_kw):
629 | """
630 | Given an :class:`http.client.HTTPResponse` instance ``r``, return a
631 | corresponding :class:`urllib3.response.HTTPResponse` object.
632 |
633 | Remaining parameters are passed to the HTTPResponse constructor, along
634 | with ``original_response=r``.
635 | """
636 | headers = r.msg
637 |
638 | if not isinstance(headers, HTTPHeaderDict):
639 | if six.PY2:
640 | # Python 2.7
641 | headers = HTTPHeaderDict.from_httplib(headers)
642 | else:
643 | headers = HTTPHeaderDict(headers.items())
644 |
645 | # HTTPResponse objects in Python 3 don't have a .strict attribute
646 | strict = getattr(r, "strict", 0)
647 | resp = ResponseCls(
648 | body=r,
649 | headers=headers,
650 | status=r.status,
651 | version=r.version,
652 | reason=r.reason,
653 | strict=strict,
654 | original_response=r,
655 | **response_kw
656 | )
657 | return resp
658 |
659 | # Backwards-compatibility methods for http.client.HTTPResponse
660 | def getheaders(self):
661 | warnings.warn(
662 | "HTTPResponse.getheaders() is deprecated and will be removed "
663 | "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.",
664 | category=DeprecationWarning,
665 | stacklevel=2,
666 | )
667 | return self.headers
668 |
669 | def getheader(self, name, default=None):
670 | warnings.warn(
671 | "HTTPResponse.getheader() is deprecated and will be removed "
672 | "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).",
673 | category=DeprecationWarning,
674 | stacklevel=2,
675 | )
676 | return self.headers.get(name, default)
677 |
678 | # Backwards compatibility for http.cookiejar
679 | def info(self):
680 | return self.headers
681 |
682 | # Overrides from io.IOBase
683 | def close(self):
684 | if not self.closed:
685 | self._fp.close()
686 |
687 | if self._connection:
688 | self._connection.close()
689 |
690 | if not self.auto_close:
691 | io.IOBase.close(self)
692 |
693 | @property
694 | def closed(self):
695 | if not self.auto_close:
696 | return io.IOBase.closed.__get__(self)
697 | elif self._fp is None:
698 | return True
699 | elif hasattr(self._fp, "isclosed"):
700 | return self._fp.isclosed()
701 | elif hasattr(self._fp, "closed"):
702 | return self._fp.closed
703 | else:
704 | return True
705 |
706 | def fileno(self):
707 | if self._fp is None:
708 | raise IOError("HTTPResponse has no file to get a fileno from")
709 | elif hasattr(self._fp, "fileno"):
710 | return self._fp.fileno()
711 | else:
712 | raise IOError(
713 | "The file-like object this HTTPResponse is wrapped "
714 | "around has no file descriptor"
715 | )
716 |
717 | def flush(self):
718 | if (
719 | self._fp is not None
720 | and hasattr(self._fp, "flush")
721 | and not getattr(self._fp, "closed", False)
722 | ):
723 | return self._fp.flush()
724 |
725 | def readable(self):
726 | # This method is required for `io` module compatibility.
727 | return True
728 |
729 | def readinto(self, b):
730 | # This method is required for `io` module compatibility.
731 | temp = self.read(len(b))
732 | if len(temp) == 0:
733 | return 0
734 | else:
735 | b[: len(temp)] = temp
736 | return len(temp)
737 |
738 | def supports_chunked_reads(self):
739 | """
740 | Checks if the underlying file-like object looks like a
741 | :class:`http.client.HTTPResponse` object. We do this by testing for
742 | the fp attribute. If it is present we assume it returns raw chunks as
743 | processed by read_chunked().
744 | """
745 | return hasattr(self._fp, "fp")
746 |
747 | def _update_chunk_length(self):
748 | # First, we'll figure out length of a chunk and then
749 | # we'll try to read it from socket.
750 | if self.chunk_left is not None:
751 | return
752 | line = self._fp.fp.readline()
753 | line = line.split(b";", 1)[0]
754 | try:
755 | self.chunk_left = int(line, 16)
756 | except ValueError:
757 | # Invalid chunked protocol response, abort.
758 | self.close()
759 | raise InvalidChunkLength(self, line)
760 |
761 | def _handle_chunk(self, amt):
762 | returned_chunk = None
763 | if amt is None:
764 | chunk = self._fp._safe_read(self.chunk_left)
765 | returned_chunk = chunk
766 | self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
767 | self.chunk_left = None
768 | elif amt < self.chunk_left:
769 | value = self._fp._safe_read(amt)
770 | self.chunk_left = self.chunk_left - amt
771 | returned_chunk = value
772 | elif amt == self.chunk_left:
773 | value = self._fp._safe_read(amt)
774 | self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
775 | self.chunk_left = None
776 | returned_chunk = value
777 | else: # amt > self.chunk_left
778 | returned_chunk = self._fp._safe_read(self.chunk_left)
779 | self._fp._safe_read(2) # Toss the CRLF at the end of the chunk.
780 | self.chunk_left = None
781 | return returned_chunk
782 |
783 | def read_chunked(self, amt=None, decode_content=None):
784 | """
785 | Similar to :meth:`HTTPResponse.read`, but with an additional
786 | parameter: ``decode_content``.
787 |
788 | :param amt:
789 | How much of the content to read. If specified, caching is skipped
790 | because it doesn't make sense to cache partial content as the full
791 | response.
792 |
793 | :param decode_content:
794 | If True, will attempt to decode the body based on the
795 | 'content-encoding' header.
796 | """
797 | self._init_decoder()
798 | # FIXME: Rewrite this method and make it a class with a better structured logic.
799 | if not self.chunked:
800 | raise ResponseNotChunked(
801 | "Response is not chunked. "
802 | "Header 'transfer-encoding: chunked' is missing."
803 | )
804 | if not self.supports_chunked_reads():
805 | raise BodyNotHttplibCompatible(
806 | "Body should be http.client.HTTPResponse like. "
807 | "It should have have an fp attribute which returns raw chunks."
808 | )
809 |
810 | with self._error_catcher():
811 | # Don't bother reading the body of a HEAD request.
812 | if self._original_response and is_response_to_head(self._original_response):
813 | self._original_response.close()
814 | return
815 |
816 | # If a response is already read and closed
817 | # then return immediately.
818 | if self._fp.fp is None:
819 | return
820 |
821 | while True:
822 | self._update_chunk_length()
823 | if self.chunk_left == 0:
824 | break
825 | chunk = self._handle_chunk(amt)
826 | decoded = self._decode(
827 | chunk, decode_content=decode_content, flush_decoder=False
828 | )
829 | if decoded:
830 | yield decoded
831 |
832 | if decode_content:
833 | # On CPython and PyPy, we should never need to flush the
834 | # decoder. However, on Jython we *might* need to, so
835 | # lets defensively do it anyway.
836 | decoded = self._flush_decoder()
837 | if decoded: # Platform-specific: Jython.
838 | yield decoded
839 |
840 | # Chunk content ends with \r\n: discard it.
841 | while True:
842 | line = self._fp.fp.readline()
843 | if not line:
844 | # Some sites may not end with '\r\n'.
845 | break
846 | if line == b"\r\n":
847 | break
848 |
849 | # We read everything; close the "file".
850 | if self._original_response:
851 | self._original_response.close()
852 |
853 | def geturl(self):
854 | """
855 | Returns the URL that was the source of this response.
856 | If the request that generated this response redirected, this method
857 | will return the final redirect location.
858 | """
859 | if self.retries is not None and len(self.retries.history):
860 | return self.retries.history[-1].redirect_location
861 | else:
862 | return self._request_url
863 |
864 | def __iter__(self):
865 | buffer = []
866 | for chunk in self.stream(decode_content=True):
867 | if b"\n" in chunk:
868 | chunk = chunk.split(b"\n")
869 | yield b"".join(buffer) + chunk[0] + b"\n"
870 | for x in chunk[1:-1]:
871 | yield x + b"\n"
872 | if chunk[-1]:
873 | buffer = [chunk[-1]]
874 | else:
875 | buffer = []
876 | else:
877 | buffer.append(chunk)
878 | if buffer:
879 | yield b"".join(buffer)
880 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/jinja2/ext.py:
--------------------------------------------------------------------------------
```python
1 | """Extension API for adding custom tags and behavior."""
2 |
3 | import pprint
4 | import re
5 | import typing as t
6 |
7 | from markupsafe import Markup
8 |
9 | from . import defaults
10 | from . import nodes
11 | from .environment import Environment
12 | from .exceptions import TemplateAssertionError
13 | from .exceptions import TemplateSyntaxError
14 | from .runtime import concat # type: ignore
15 | from .runtime import Context
16 | from .runtime import Undefined
17 | from .utils import import_string
18 | from .utils import pass_context
19 |
20 | if t.TYPE_CHECKING:
21 | import typing_extensions as te
22 |
23 | from .lexer import Token
24 | from .lexer import TokenStream
25 | from .parser import Parser
26 |
27 | class _TranslationsBasic(te.Protocol):
28 | def gettext(self, message: str) -> str: ...
29 |
30 | def ngettext(self, singular: str, plural: str, n: int) -> str:
31 | pass
32 |
33 | class _TranslationsContext(_TranslationsBasic):
34 | def pgettext(self, context: str, message: str) -> str: ...
35 |
36 | def npgettext(
37 | self, context: str, singular: str, plural: str, n: int
38 | ) -> str: ...
39 |
40 | _SupportedTranslations = t.Union[_TranslationsBasic, _TranslationsContext]
41 |
42 |
43 | # I18N functions available in Jinja templates. If the I18N library
44 | # provides ugettext, it will be assigned to gettext.
45 | GETTEXT_FUNCTIONS: t.Tuple[str, ...] = (
46 | "_",
47 | "gettext",
48 | "ngettext",
49 | "pgettext",
50 | "npgettext",
51 | )
52 | _ws_re = re.compile(r"\s*\n\s*")
53 |
54 |
55 | class Extension:
56 | """Extensions can be used to add extra functionality to the Jinja template
57 | system at the parser level. Custom extensions are bound to an environment
58 | but may not store environment specific data on `self`. The reason for
59 | this is that an extension can be bound to another environment (for
60 | overlays) by creating a copy and reassigning the `environment` attribute.
61 |
62 | As extensions are created by the environment they cannot accept any
63 | arguments for configuration. One may want to work around that by using
64 | a factory function, but that is not possible as extensions are identified
65 | by their import name. The correct way to configure the extension is
66 | storing the configuration values on the environment. Because this way the
67 | environment ends up acting as central configuration storage the
68 | attributes may clash which is why extensions have to ensure that the names
69 | they choose for configuration are not too generic. ``prefix`` for example
70 | is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
71 | name as includes the name of the extension (fragment cache).
72 | """
73 |
74 | identifier: t.ClassVar[str]
75 |
76 | def __init_subclass__(cls) -> None:
77 | cls.identifier = f"{cls.__module__}.{cls.__name__}"
78 |
79 | #: if this extension parses this is the list of tags it's listening to.
80 | tags: t.Set[str] = set()
81 |
82 | #: the priority of that extension. This is especially useful for
83 | #: extensions that preprocess values. A lower value means higher
84 | #: priority.
85 | #:
86 | #: .. versionadded:: 2.4
87 | priority = 100
88 |
89 | def __init__(self, environment: Environment) -> None:
90 | self.environment = environment
91 |
92 | def bind(self, environment: Environment) -> "Extension":
93 | """Create a copy of this extension bound to another environment."""
94 | rv = object.__new__(self.__class__)
95 | rv.__dict__.update(self.__dict__)
96 | rv.environment = environment
97 | return rv
98 |
99 | def preprocess(
100 | self, source: str, name: t.Optional[str], filename: t.Optional[str] = None
101 | ) -> str:
102 | """This method is called before the actual lexing and can be used to
103 | preprocess the source. The `filename` is optional. The return value
104 | must be the preprocessed source.
105 | """
106 | return source
107 |
108 | def filter_stream(
109 | self, stream: "TokenStream"
110 | ) -> t.Union["TokenStream", t.Iterable["Token"]]:
111 | """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
112 | to filter tokens returned. This method has to return an iterable of
113 | :class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
114 | :class:`~jinja2.lexer.TokenStream`.
115 | """
116 | return stream
117 |
118 | def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
119 | """If any of the :attr:`tags` matched this method is called with the
120 | parser as first argument. The token the parser stream is pointing at
121 | is the name token that matched. This method has to return one or a
122 | list of multiple nodes.
123 | """
124 | raise NotImplementedError()
125 |
126 | def attr(
127 | self, name: str, lineno: t.Optional[int] = None
128 | ) -> nodes.ExtensionAttribute:
129 | """Return an attribute node for the current extension. This is useful
130 | to pass constants on extensions to generated template code.
131 |
132 | ::
133 |
134 | self.attr('_my_attribute', lineno=lineno)
135 | """
136 | return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
137 |
138 | def call_method(
139 | self,
140 | name: str,
141 | args: t.Optional[t.List[nodes.Expr]] = None,
142 | kwargs: t.Optional[t.List[nodes.Keyword]] = None,
143 | dyn_args: t.Optional[nodes.Expr] = None,
144 | dyn_kwargs: t.Optional[nodes.Expr] = None,
145 | lineno: t.Optional[int] = None,
146 | ) -> nodes.Call:
147 | """Call a method of the extension. This is a shortcut for
148 | :meth:`attr` + :class:`jinja2.nodes.Call`.
149 | """
150 | if args is None:
151 | args = []
152 | if kwargs is None:
153 | kwargs = []
154 | return nodes.Call(
155 | self.attr(name, lineno=lineno),
156 | args,
157 | kwargs,
158 | dyn_args,
159 | dyn_kwargs,
160 | lineno=lineno,
161 | )
162 |
163 |
164 | @pass_context
165 | def _gettext_alias(
166 | __context: Context, *args: t.Any, **kwargs: t.Any
167 | ) -> t.Union[t.Any, Undefined]:
168 | return __context.call(__context.resolve("gettext"), *args, **kwargs)
169 |
170 |
171 | def _make_new_gettext(func: t.Callable[[str], str]) -> t.Callable[..., str]:
172 | @pass_context
173 | def gettext(__context: Context, __string: str, **variables: t.Any) -> str:
174 | rv = __context.call(func, __string)
175 | if __context.eval_ctx.autoescape:
176 | rv = Markup(rv)
177 | # Always treat as a format string, even if there are no
178 | # variables. This makes translation strings more consistent
179 | # and predictable. This requires escaping
180 | return rv % variables # type: ignore
181 |
182 | return gettext
183 |
184 |
185 | def _make_new_ngettext(func: t.Callable[[str, str, int], str]) -> t.Callable[..., str]:
186 | @pass_context
187 | def ngettext(
188 | __context: Context,
189 | __singular: str,
190 | __plural: str,
191 | __num: int,
192 | **variables: t.Any,
193 | ) -> str:
194 | variables.setdefault("num", __num)
195 | rv = __context.call(func, __singular, __plural, __num)
196 | if __context.eval_ctx.autoescape:
197 | rv = Markup(rv)
198 | # Always treat as a format string, see gettext comment above.
199 | return rv % variables # type: ignore
200 |
201 | return ngettext
202 |
203 |
204 | def _make_new_pgettext(func: t.Callable[[str, str], str]) -> t.Callable[..., str]:
205 | @pass_context
206 | def pgettext(
207 | __context: Context, __string_ctx: str, __string: str, **variables: t.Any
208 | ) -> str:
209 | variables.setdefault("context", __string_ctx)
210 | rv = __context.call(func, __string_ctx, __string)
211 |
212 | if __context.eval_ctx.autoescape:
213 | rv = Markup(rv)
214 |
215 | # Always treat as a format string, see gettext comment above.
216 | return rv % variables # type: ignore
217 |
218 | return pgettext
219 |
220 |
221 | def _make_new_npgettext(
222 | func: t.Callable[[str, str, str, int], str],
223 | ) -> t.Callable[..., str]:
224 | @pass_context
225 | def npgettext(
226 | __context: Context,
227 | __string_ctx: str,
228 | __singular: str,
229 | __plural: str,
230 | __num: int,
231 | **variables: t.Any,
232 | ) -> str:
233 | variables.setdefault("context", __string_ctx)
234 | variables.setdefault("num", __num)
235 | rv = __context.call(func, __string_ctx, __singular, __plural, __num)
236 |
237 | if __context.eval_ctx.autoescape:
238 | rv = Markup(rv)
239 |
240 | # Always treat as a format string, see gettext comment above.
241 | return rv % variables # type: ignore
242 |
243 | return npgettext
244 |
245 |
246 | class InternationalizationExtension(Extension):
247 | """This extension adds gettext support to Jinja."""
248 |
249 | tags = {"trans"}
250 |
251 | # TODO: the i18n extension is currently reevaluating values in a few
252 | # situations. Take this example:
253 | # {% trans count=something() %}{{ count }} foo{% pluralize
254 | # %}{{ count }} fooss{% endtrans %}
255 | # something is called twice here. One time for the gettext value and
256 | # the other time for the n-parameter of the ngettext function.
257 |
258 | def __init__(self, environment: Environment) -> None:
259 | super().__init__(environment)
260 | environment.globals["_"] = _gettext_alias
261 | environment.extend(
262 | install_gettext_translations=self._install,
263 | install_null_translations=self._install_null,
264 | install_gettext_callables=self._install_callables,
265 | uninstall_gettext_translations=self._uninstall,
266 | extract_translations=self._extract,
267 | newstyle_gettext=False,
268 | )
269 |
270 | def _install(
271 | self, translations: "_SupportedTranslations", newstyle: t.Optional[bool] = None
272 | ) -> None:
273 | # ugettext and ungettext are preferred in case the I18N library
274 | # is providing compatibility with older Python versions.
275 | gettext = getattr(translations, "ugettext", None)
276 | if gettext is None:
277 | gettext = translations.gettext
278 | ngettext = getattr(translations, "ungettext", None)
279 | if ngettext is None:
280 | ngettext = translations.ngettext
281 |
282 | pgettext = getattr(translations, "pgettext", None)
283 | npgettext = getattr(translations, "npgettext", None)
284 | self._install_callables(
285 | gettext, ngettext, newstyle=newstyle, pgettext=pgettext, npgettext=npgettext
286 | )
287 |
288 | def _install_null(self, newstyle: t.Optional[bool] = None) -> None:
289 | import gettext
290 |
291 | translations = gettext.NullTranslations()
292 |
293 | if hasattr(translations, "pgettext"):
294 | # Python < 3.8
295 | pgettext = translations.pgettext
296 | else:
297 |
298 | def pgettext(c: str, s: str) -> str: # type: ignore[misc]
299 | return s
300 |
301 | if hasattr(translations, "npgettext"):
302 | npgettext = translations.npgettext
303 | else:
304 |
305 | def npgettext(c: str, s: str, p: str, n: int) -> str: # type: ignore[misc]
306 | return s if n == 1 else p
307 |
308 | self._install_callables(
309 | gettext=translations.gettext,
310 | ngettext=translations.ngettext,
311 | newstyle=newstyle,
312 | pgettext=pgettext,
313 | npgettext=npgettext,
314 | )
315 |
316 | def _install_callables(
317 | self,
318 | gettext: t.Callable[[str], str],
319 | ngettext: t.Callable[[str, str, int], str],
320 | newstyle: t.Optional[bool] = None,
321 | pgettext: t.Optional[t.Callable[[str, str], str]] = None,
322 | npgettext: t.Optional[t.Callable[[str, str, str, int], str]] = None,
323 | ) -> None:
324 | if newstyle is not None:
325 | self.environment.newstyle_gettext = newstyle # type: ignore
326 | if self.environment.newstyle_gettext: # type: ignore
327 | gettext = _make_new_gettext(gettext)
328 | ngettext = _make_new_ngettext(ngettext)
329 |
330 | if pgettext is not None:
331 | pgettext = _make_new_pgettext(pgettext)
332 |
333 | if npgettext is not None:
334 | npgettext = _make_new_npgettext(npgettext)
335 |
336 | self.environment.globals.update(
337 | gettext=gettext, ngettext=ngettext, pgettext=pgettext, npgettext=npgettext
338 | )
339 |
340 | def _uninstall(self, translations: "_SupportedTranslations") -> None:
341 | for key in ("gettext", "ngettext", "pgettext", "npgettext"):
342 | self.environment.globals.pop(key, None)
343 |
344 | def _extract(
345 | self,
346 | source: t.Union[str, nodes.Template],
347 | gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
348 | ) -> t.Iterator[
349 | t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
350 | ]:
351 | if isinstance(source, str):
352 | source = self.environment.parse(source)
353 | return extract_from_ast(source, gettext_functions)
354 |
355 | def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
356 | """Parse a translatable tag."""
357 | lineno = next(parser.stream).lineno
358 |
359 | context = None
360 | context_token = parser.stream.next_if("string")
361 |
362 | if context_token is not None:
363 | context = context_token.value
364 |
365 | # find all the variables referenced. Additionally a variable can be
366 | # defined in the body of the trans block too, but this is checked at
367 | # a later state.
368 | plural_expr: t.Optional[nodes.Expr] = None
369 | plural_expr_assignment: t.Optional[nodes.Assign] = None
370 | num_called_num = False
371 | variables: t.Dict[str, nodes.Expr] = {}
372 | trimmed = None
373 | while parser.stream.current.type != "block_end":
374 | if variables:
375 | parser.stream.expect("comma")
376 |
377 | # skip colon for python compatibility
378 | if parser.stream.skip_if("colon"):
379 | break
380 |
381 | token = parser.stream.expect("name")
382 | if token.value in variables:
383 | parser.fail(
384 | f"translatable variable {token.value!r} defined twice.",
385 | token.lineno,
386 | exc=TemplateAssertionError,
387 | )
388 |
389 | # expressions
390 | if parser.stream.current.type == "assign":
391 | next(parser.stream)
392 | variables[token.value] = var = parser.parse_expression()
393 | elif trimmed is None and token.value in ("trimmed", "notrimmed"):
394 | trimmed = token.value == "trimmed"
395 | continue
396 | else:
397 | variables[token.value] = var = nodes.Name(token.value, "load")
398 |
399 | if plural_expr is None:
400 | if isinstance(var, nodes.Call):
401 | plural_expr = nodes.Name("_trans", "load")
402 | variables[token.value] = plural_expr
403 | plural_expr_assignment = nodes.Assign(
404 | nodes.Name("_trans", "store"), var
405 | )
406 | else:
407 | plural_expr = var
408 | num_called_num = token.value == "num"
409 |
410 | parser.stream.expect("block_end")
411 |
412 | plural = None
413 | have_plural = False
414 | referenced = set()
415 |
416 | # now parse until endtrans or pluralize
417 | singular_names, singular = self._parse_block(parser, True)
418 | if singular_names:
419 | referenced.update(singular_names)
420 | if plural_expr is None:
421 | plural_expr = nodes.Name(singular_names[0], "load")
422 | num_called_num = singular_names[0] == "num"
423 |
424 | # if we have a pluralize block, we parse that too
425 | if parser.stream.current.test("name:pluralize"):
426 | have_plural = True
427 | next(parser.stream)
428 | if parser.stream.current.type != "block_end":
429 | token = parser.stream.expect("name")
430 | if token.value not in variables:
431 | parser.fail(
432 | f"unknown variable {token.value!r} for pluralization",
433 | token.lineno,
434 | exc=TemplateAssertionError,
435 | )
436 | plural_expr = variables[token.value]
437 | num_called_num = token.value == "num"
438 | parser.stream.expect("block_end")
439 | plural_names, plural = self._parse_block(parser, False)
440 | next(parser.stream)
441 | referenced.update(plural_names)
442 | else:
443 | next(parser.stream)
444 |
445 | # register free names as simple name expressions
446 | for name in referenced:
447 | if name not in variables:
448 | variables[name] = nodes.Name(name, "load")
449 |
450 | if not have_plural:
451 | plural_expr = None
452 | elif plural_expr is None:
453 | parser.fail("pluralize without variables", lineno)
454 |
455 | if trimmed is None:
456 | trimmed = self.environment.policies["ext.i18n.trimmed"]
457 | if trimmed:
458 | singular = self._trim_whitespace(singular)
459 | if plural:
460 | plural = self._trim_whitespace(plural)
461 |
462 | node = self._make_node(
463 | singular,
464 | plural,
465 | context,
466 | variables,
467 | plural_expr,
468 | bool(referenced),
469 | num_called_num and have_plural,
470 | )
471 | node.set_lineno(lineno)
472 | if plural_expr_assignment is not None:
473 | return [plural_expr_assignment, node]
474 | else:
475 | return node
476 |
477 | def _trim_whitespace(self, string: str, _ws_re: t.Pattern[str] = _ws_re) -> str:
478 | return _ws_re.sub(" ", string.strip())
479 |
480 | def _parse_block(
481 | self, parser: "Parser", allow_pluralize: bool
482 | ) -> t.Tuple[t.List[str], str]:
483 | """Parse until the next block tag with a given name."""
484 | referenced = []
485 | buf = []
486 |
487 | while True:
488 | if parser.stream.current.type == "data":
489 | buf.append(parser.stream.current.value.replace("%", "%%"))
490 | next(parser.stream)
491 | elif parser.stream.current.type == "variable_begin":
492 | next(parser.stream)
493 | name = parser.stream.expect("name").value
494 | referenced.append(name)
495 | buf.append(f"%({name})s")
496 | parser.stream.expect("variable_end")
497 | elif parser.stream.current.type == "block_begin":
498 | next(parser.stream)
499 | block_name = (
500 | parser.stream.current.value
501 | if parser.stream.current.type == "name"
502 | else None
503 | )
504 | if block_name == "endtrans":
505 | break
506 | elif block_name == "pluralize":
507 | if allow_pluralize:
508 | break
509 | parser.fail(
510 | "a translatable section can have only one pluralize section"
511 | )
512 | elif block_name == "trans":
513 | parser.fail(
514 | "trans blocks can't be nested; did you mean `endtrans`?"
515 | )
516 | parser.fail(
517 | f"control structures in translatable sections are not allowed; "
518 | f"saw `{block_name}`"
519 | )
520 | elif parser.stream.eos:
521 | parser.fail("unclosed translation block")
522 | else:
523 | raise RuntimeError("internal parser error")
524 |
525 | return referenced, concat(buf)
526 |
527 | def _make_node(
528 | self,
529 | singular: str,
530 | plural: t.Optional[str],
531 | context: t.Optional[str],
532 | variables: t.Dict[str, nodes.Expr],
533 | plural_expr: t.Optional[nodes.Expr],
534 | vars_referenced: bool,
535 | num_called_num: bool,
536 | ) -> nodes.Output:
537 | """Generates a useful node from the data provided."""
538 | newstyle = self.environment.newstyle_gettext # type: ignore
539 | node: nodes.Expr
540 |
541 | # no variables referenced? no need to escape for old style
542 | # gettext invocations only if there are vars.
543 | if not vars_referenced and not newstyle:
544 | singular = singular.replace("%%", "%")
545 | if plural:
546 | plural = plural.replace("%%", "%")
547 |
548 | func_name = "gettext"
549 | func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
550 |
551 | if context is not None:
552 | func_args.insert(0, nodes.Const(context))
553 | func_name = f"p{func_name}"
554 |
555 | if plural_expr is not None:
556 | func_name = f"n{func_name}"
557 | func_args.extend((nodes.Const(plural), plural_expr))
558 |
559 | node = nodes.Call(nodes.Name(func_name, "load"), func_args, [], None, None)
560 |
561 | # in case newstyle gettext is used, the method is powerful
562 | # enough to handle the variable expansion and autoescape
563 | # handling itself
564 | if newstyle:
565 | for key, value in variables.items():
566 | # the function adds that later anyways in case num was
567 | # called num, so just skip it.
568 | if num_called_num and key == "num":
569 | continue
570 | node.kwargs.append(nodes.Keyword(key, value))
571 |
572 | # otherwise do that here
573 | else:
574 | # mark the return value as safe if we are in an
575 | # environment with autoescaping turned on
576 | node = nodes.MarkSafeIfAutoescape(node)
577 | if variables:
578 | node = nodes.Mod(
579 | node,
580 | nodes.Dict(
581 | [
582 | nodes.Pair(nodes.Const(key), value)
583 | for key, value in variables.items()
584 | ]
585 | ),
586 | )
587 | return nodes.Output([node])
588 |
589 |
590 | class ExprStmtExtension(Extension):
591 | """Adds a `do` tag to Jinja that works like the print statement just
592 | that it doesn't print the return value.
593 | """
594 |
595 | tags = {"do"}
596 |
597 | def parse(self, parser: "Parser") -> nodes.ExprStmt:
598 | node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
599 | node.node = parser.parse_tuple()
600 | return node
601 |
602 |
603 | class LoopControlExtension(Extension):
604 | """Adds break and continue to the template engine."""
605 |
606 | tags = {"break", "continue"}
607 |
608 | def parse(self, parser: "Parser") -> t.Union[nodes.Break, nodes.Continue]:
609 | token = next(parser.stream)
610 | if token.value == "break":
611 | return nodes.Break(lineno=token.lineno)
612 | return nodes.Continue(lineno=token.lineno)
613 |
614 |
615 | class DebugExtension(Extension):
616 | """A ``{% debug %}`` tag that dumps the available variables,
617 | filters, and tests.
618 |
619 | .. code-block:: html+jinja
620 |
621 | <pre>{% debug %}</pre>
622 |
623 | .. code-block:: text
624 |
625 | {'context': {'cycler': <class 'jinja2.utils.Cycler'>,
626 | ...,
627 | 'namespace': <class 'jinja2.utils.Namespace'>},
628 | 'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
629 | ..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
630 | 'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
631 | ..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}
632 |
633 | .. versionadded:: 2.11.0
634 | """
635 |
636 | tags = {"debug"}
637 |
638 | def parse(self, parser: "Parser") -> nodes.Output:
639 | lineno = parser.stream.expect("name:debug").lineno
640 | context = nodes.ContextReference()
641 | result = self.call_method("_render", [context], lineno=lineno)
642 | return nodes.Output([result], lineno=lineno)
643 |
644 | def _render(self, context: Context) -> str:
645 | result = {
646 | "context": context.get_all(),
647 | "filters": sorted(self.environment.filters.keys()),
648 | "tests": sorted(self.environment.tests.keys()),
649 | }
650 |
651 | # Set the depth since the intent is to show the top few names.
652 | return pprint.pformat(result, depth=3, compact=True)
653 |
654 |
655 | def extract_from_ast(
656 | ast: nodes.Template,
657 | gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
658 | babel_style: bool = True,
659 | ) -> t.Iterator[
660 | t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
661 | ]:
662 | """Extract localizable strings from the given template node. Per
663 | default this function returns matches in babel style that means non string
664 | parameters as well as keyword arguments are returned as `None`. This
665 | allows Babel to figure out what you really meant if you are using
666 | gettext functions that allow keyword arguments for placeholder expansion.
667 | If you don't want that behavior set the `babel_style` parameter to `False`
668 | which causes only strings to be returned and parameters are always stored
669 | in tuples. As a consequence invalid gettext calls (calls without a single
670 | string parameter or string parameters after non-string parameters) are
671 | skipped.
672 |
673 | This example explains the behavior:
674 |
675 | >>> from jinja2 import Environment
676 | >>> env = Environment()
677 | >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
678 | >>> list(extract_from_ast(node))
679 | [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
680 | >>> list(extract_from_ast(node, babel_style=False))
681 | [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
682 |
683 | For every string found this function yields a ``(lineno, function,
684 | message)`` tuple, where:
685 |
686 | * ``lineno`` is the number of the line on which the string was found,
687 | * ``function`` is the name of the ``gettext`` function used (if the
688 | string was extracted from embedded Python code), and
689 | * ``message`` is the string, or a tuple of strings for functions
690 | with multiple string arguments.
691 |
692 | This extraction function operates on the AST and is because of that unable
693 | to extract any comments. For comment support you have to use the babel
694 | extraction interface or extract comments yourself.
695 | """
696 | out: t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]
697 |
698 | for node in ast.find_all(nodes.Call):
699 | if (
700 | not isinstance(node.node, nodes.Name)
701 | or node.node.name not in gettext_functions
702 | ):
703 | continue
704 |
705 | strings: t.List[t.Optional[str]] = []
706 |
707 | for arg in node.args:
708 | if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
709 | strings.append(arg.value)
710 | else:
711 | strings.append(None)
712 |
713 | for _ in node.kwargs:
714 | strings.append(None)
715 | if node.dyn_args is not None:
716 | strings.append(None)
717 | if node.dyn_kwargs is not None:
718 | strings.append(None)
719 |
720 | if not babel_style:
721 | out = tuple(x for x in strings if x is not None)
722 |
723 | if not out:
724 | continue
725 | else:
726 | if len(strings) == 1:
727 | out = strings[0]
728 | else:
729 | out = tuple(strings)
730 |
731 | yield node.lineno, node.node.name, out
732 |
733 |
734 | class _CommentFinder:
735 | """Helper class to find comments in a token stream. Can only
736 | find comments for gettext calls forwards. Once the comment
737 | from line 4 is found, a comment for line 1 will not return a
738 | usable value.
739 | """
740 |
741 | def __init__(
742 | self, tokens: t.Sequence[t.Tuple[int, str, str]], comment_tags: t.Sequence[str]
743 | ) -> None:
744 | self.tokens = tokens
745 | self.comment_tags = comment_tags
746 | self.offset = 0
747 | self.last_lineno = 0
748 |
749 | def find_backwards(self, offset: int) -> t.List[str]:
750 | try:
751 | for _, token_type, token_value in reversed(
752 | self.tokens[self.offset : offset]
753 | ):
754 | if token_type in ("comment", "linecomment"):
755 | try:
756 | prefix, comment = token_value.split(None, 1)
757 | except ValueError:
758 | continue
759 | if prefix in self.comment_tags:
760 | return [comment.rstrip()]
761 | return []
762 | finally:
763 | self.offset = offset
764 |
765 | def find_comments(self, lineno: int) -> t.List[str]:
766 | if not self.comment_tags or self.last_lineno > lineno:
767 | return []
768 | for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
769 | if token_lineno > lineno:
770 | return self.find_backwards(self.offset + idx)
771 | return self.find_backwards(len(self.tokens))
772 |
773 |
774 | def babel_extract(
775 | fileobj: t.BinaryIO,
776 | keywords: t.Sequence[str],
777 | comment_tags: t.Sequence[str],
778 | options: t.Dict[str, t.Any],
779 | ) -> t.Iterator[
780 | t.Tuple[
781 | int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]], t.List[str]
782 | ]
783 | ]:
784 | """Babel extraction method for Jinja templates.
785 |
786 | .. versionchanged:: 2.3
787 | Basic support for translation comments was added. If `comment_tags`
788 | is now set to a list of keywords for extraction, the extractor will
789 | try to find the best preceding comment that begins with one of the
790 | keywords. For best results, make sure to not have more than one
791 | gettext call in one line of code and the matching comment in the
792 | same line or the line before.
793 |
794 | .. versionchanged:: 2.5.1
795 | The `newstyle_gettext` flag can be set to `True` to enable newstyle
796 | gettext calls.
797 |
798 | .. versionchanged:: 2.7
799 | A `silent` option can now be provided. If set to `False` template
800 | syntax errors are propagated instead of being ignored.
801 |
802 | :param fileobj: the file-like object the messages should be extracted from
803 | :param keywords: a list of keywords (i.e. function names) that should be
804 | recognized as translation functions
805 | :param comment_tags: a list of translator tags to search for and include
806 | in the results.
807 | :param options: a dictionary of additional options (optional)
808 | :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
809 | (comments will be empty currently)
810 | """
811 | extensions: t.Dict[t.Type[Extension], None] = {}
812 |
813 | for extension_name in options.get("extensions", "").split(","):
814 | extension_name = extension_name.strip()
815 |
816 | if not extension_name:
817 | continue
818 |
819 | extensions[import_string(extension_name)] = None
820 |
821 | if InternationalizationExtension not in extensions:
822 | extensions[InternationalizationExtension] = None
823 |
824 | def getbool(options: t.Mapping[str, str], key: str, default: bool = False) -> bool:
825 | return options.get(key, str(default)).lower() in {"1", "on", "yes", "true"}
826 |
827 | silent = getbool(options, "silent", True)
828 | environment = Environment(
829 | options.get("block_start_string", defaults.BLOCK_START_STRING),
830 | options.get("block_end_string", defaults.BLOCK_END_STRING),
831 | options.get("variable_start_string", defaults.VARIABLE_START_STRING),
832 | options.get("variable_end_string", defaults.VARIABLE_END_STRING),
833 | options.get("comment_start_string", defaults.COMMENT_START_STRING),
834 | options.get("comment_end_string", defaults.COMMENT_END_STRING),
835 | options.get("line_statement_prefix") or defaults.LINE_STATEMENT_PREFIX,
836 | options.get("line_comment_prefix") or defaults.LINE_COMMENT_PREFIX,
837 | getbool(options, "trim_blocks", defaults.TRIM_BLOCKS),
838 | getbool(options, "lstrip_blocks", defaults.LSTRIP_BLOCKS),
839 | defaults.NEWLINE_SEQUENCE,
840 | getbool(options, "keep_trailing_newline", defaults.KEEP_TRAILING_NEWLINE),
841 | tuple(extensions),
842 | cache_size=0,
843 | auto_reload=False,
844 | )
845 |
846 | if getbool(options, "trimmed"):
847 | environment.policies["ext.i18n.trimmed"] = True
848 | if getbool(options, "newstyle_gettext"):
849 | environment.newstyle_gettext = True # type: ignore
850 |
851 | source = fileobj.read().decode(options.get("encoding", "utf-8"))
852 | try:
853 | node = environment.parse(source)
854 | tokens = list(environment.lex(environment.preprocess(source)))
855 | except TemplateSyntaxError:
856 | if not silent:
857 | raise
858 | # skip templates with syntax errors
859 | return
860 |
861 | finder = _CommentFinder(tokens, comment_tags)
862 | for lineno, func, message in extract_from_ast(node, keywords):
863 | yield lineno, func, message, finder.find_comments(lineno)
864 |
865 |
866 | #: nicer import names
867 | i18n = InternationalizationExtension
868 | do = ExprStmtExtension
869 | loopcontrols = LoopControlExtension
870 | debug = DebugExtension
871 |
```