This is page 148 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/jinja2/filters.py:
--------------------------------------------------------------------------------
```python
1 | """Built-in template filters used with the ``|`` operator."""
2 |
3 | import math
4 | import random
5 | import re
6 | import typing
7 | import typing as t
8 | from collections import abc
9 | from itertools import chain
10 | from itertools import groupby
11 |
12 | from markupsafe import escape
13 | from markupsafe import Markup
14 | from markupsafe import soft_str
15 |
16 | from .async_utils import async_variant
17 | from .async_utils import auto_aiter
18 | from .async_utils import auto_await
19 | from .async_utils import auto_to_list
20 | from .exceptions import FilterArgumentError
21 | from .runtime import Undefined
22 | from .utils import htmlsafe_json_dumps
23 | from .utils import pass_context
24 | from .utils import pass_environment
25 | from .utils import pass_eval_context
26 | from .utils import pformat
27 | from .utils import url_quote
28 | from .utils import urlize
29 |
30 | if t.TYPE_CHECKING:
31 | import typing_extensions as te
32 |
33 | from .environment import Environment
34 | from .nodes import EvalContext
35 | from .runtime import Context
36 | from .sandbox import SandboxedEnvironment # noqa: F401
37 |
38 | class HasHTML(te.Protocol):
39 | def __html__(self) -> str:
40 | pass
41 |
42 |
43 | F = t.TypeVar("F", bound=t.Callable[..., t.Any])
44 | K = t.TypeVar("K")
45 | V = t.TypeVar("V")
46 |
47 |
48 | def ignore_case(value: V) -> V:
49 | """For use as a postprocessor for :func:`make_attrgetter`. Converts strings
50 | to lowercase and returns other types as-is."""
51 | if isinstance(value, str):
52 | return t.cast(V, value.lower())
53 |
54 | return value
55 |
56 |
57 | def make_attrgetter(
58 | environment: "Environment",
59 | attribute: t.Optional[t.Union[str, int]],
60 | postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
61 | default: t.Optional[t.Any] = None,
62 | ) -> t.Callable[[t.Any], t.Any]:
63 | """Returns a callable that looks up the given attribute from a
64 | passed object with the rules of the environment. Dots are allowed
65 | to access attributes of attributes. Integer parts in paths are
66 | looked up as integers.
67 | """
68 | parts = _prepare_attribute_parts(attribute)
69 |
70 | def attrgetter(item: t.Any) -> t.Any:
71 | for part in parts:
72 | item = environment.getitem(item, part)
73 |
74 | if default is not None and isinstance(item, Undefined):
75 | item = default
76 |
77 | if postprocess is not None:
78 | item = postprocess(item)
79 |
80 | return item
81 |
82 | return attrgetter
83 |
84 |
85 | def make_multi_attrgetter(
86 | environment: "Environment",
87 | attribute: t.Optional[t.Union[str, int]],
88 | postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
89 | ) -> t.Callable[[t.Any], t.List[t.Any]]:
90 | """Returns a callable that looks up the given comma separated
91 | attributes from a passed object with the rules of the environment.
92 | Dots are allowed to access attributes of each attribute. Integer
93 | parts in paths are looked up as integers.
94 |
95 | The value returned by the returned callable is a list of extracted
96 | attribute values.
97 |
98 | Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
99 | """
100 | if isinstance(attribute, str):
101 | split: t.Sequence[t.Union[str, int, None]] = attribute.split(",")
102 | else:
103 | split = [attribute]
104 |
105 | parts = [_prepare_attribute_parts(item) for item in split]
106 |
107 | def attrgetter(item: t.Any) -> t.List[t.Any]:
108 | items = [None] * len(parts)
109 |
110 | for i, attribute_part in enumerate(parts):
111 | item_i = item
112 |
113 | for part in attribute_part:
114 | item_i = environment.getitem(item_i, part)
115 |
116 | if postprocess is not None:
117 | item_i = postprocess(item_i)
118 |
119 | items[i] = item_i
120 |
121 | return items
122 |
123 | return attrgetter
124 |
125 |
126 | def _prepare_attribute_parts(
127 | attr: t.Optional[t.Union[str, int]],
128 | ) -> t.List[t.Union[str, int]]:
129 | if attr is None:
130 | return []
131 |
132 | if isinstance(attr, str):
133 | return [int(x) if x.isdigit() else x for x in attr.split(".")]
134 |
135 | return [attr]
136 |
137 |
138 | def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
139 | """Enforce HTML escaping. This will probably double escape variables."""
140 | if hasattr(value, "__html__"):
141 | value = t.cast("HasHTML", value).__html__()
142 |
143 | return escape(str(value))
144 |
145 |
146 | def do_urlencode(
147 | value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]],
148 | ) -> str:
149 | """Quote data for use in a URL path or query using UTF-8.
150 |
151 | Basic wrapper around :func:`urllib.parse.quote` when given a
152 | string, or :func:`urllib.parse.urlencode` for a dict or iterable.
153 |
154 | :param value: Data to quote. A string will be quoted directly. A
155 | dict or iterable of ``(key, value)`` pairs will be joined as a
156 | query string.
157 |
158 | When given a string, "/" is not quoted. HTTP servers treat "/" and
159 | "%2F" equivalently in paths. If you need quoted slashes, use the
160 | ``|replace("/", "%2F")`` filter.
161 |
162 | .. versionadded:: 2.7
163 | """
164 | if isinstance(value, str) or not isinstance(value, abc.Iterable):
165 | return url_quote(value)
166 |
167 | if isinstance(value, dict):
168 | items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
169 | else:
170 | items = value # type: ignore
171 |
172 | return "&".join(
173 | f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items
174 | )
175 |
176 |
177 | @pass_eval_context
178 | def do_replace(
179 | eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None
180 | ) -> str:
181 | """Return a copy of the value with all occurrences of a substring
182 | replaced with a new one. The first argument is the substring
183 | that should be replaced, the second is the replacement string.
184 | If the optional third argument ``count`` is given, only the first
185 | ``count`` occurrences are replaced:
186 |
187 | .. sourcecode:: jinja
188 |
189 | {{ "Hello World"|replace("Hello", "Goodbye") }}
190 | -> Goodbye World
191 |
192 | {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
193 | -> d'oh, d'oh, aaargh
194 | """
195 | if count is None:
196 | count = -1
197 |
198 | if not eval_ctx.autoescape:
199 | return str(s).replace(str(old), str(new), count)
200 |
201 | if (
202 | hasattr(old, "__html__")
203 | or hasattr(new, "__html__")
204 | and not hasattr(s, "__html__")
205 | ):
206 | s = escape(s)
207 | else:
208 | s = soft_str(s)
209 |
210 | return s.replace(soft_str(old), soft_str(new), count)
211 |
212 |
213 | def do_upper(s: str) -> str:
214 | """Convert a value to uppercase."""
215 | return soft_str(s).upper()
216 |
217 |
218 | def do_lower(s: str) -> str:
219 | """Convert a value to lowercase."""
220 | return soft_str(s).lower()
221 |
222 |
223 | def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
224 | """Return an iterator over the ``(key, value)`` items of a mapping.
225 |
226 | ``x|items`` is the same as ``x.items()``, except if ``x`` is
227 | undefined an empty iterator is returned.
228 |
229 | This filter is useful if you expect the template to be rendered with
230 | an implementation of Jinja in another programming language that does
231 | not have a ``.items()`` method on its mapping type.
232 |
233 | .. code-block:: html+jinja
234 |
235 | <dl>
236 | {% for key, value in my_dict|items %}
237 | <dt>{{ key }}
238 | <dd>{{ value }}
239 | {% endfor %}
240 | </dl>
241 |
242 | .. versionadded:: 3.1
243 | """
244 | if isinstance(value, Undefined):
245 | return
246 |
247 | if not isinstance(value, abc.Mapping):
248 | raise TypeError("Can only get item pairs from a mapping.")
249 |
250 | yield from value.items()
251 |
252 |
253 | # Check for characters that would move the parser state from key to value.
254 | # https://html.spec.whatwg.org/#attribute-name-state
255 | _attr_key_re = re.compile(r"[\s/>=]", flags=re.ASCII)
256 |
257 |
258 | @pass_eval_context
259 | def do_xmlattr(
260 | eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True
261 | ) -> str:
262 | """Create an SGML/XML attribute string based on the items in a dict.
263 |
264 | **Values** that are neither ``none`` nor ``undefined`` are automatically
265 | escaped, safely allowing untrusted user input.
266 |
267 | User input should not be used as **keys** to this filter. If any key
268 | contains a space, ``/`` solidus, ``>`` greater-than sign, or ``=`` equals
269 | sign, this fails with a ``ValueError``. Regardless of this, user input
270 | should never be used as keys to this filter, or must be separately validated
271 | first.
272 |
273 | .. sourcecode:: html+jinja
274 |
275 | <ul{{ {'class': 'my_list', 'missing': none,
276 | 'id': 'list-%d'|format(variable)}|xmlattr }}>
277 | ...
278 | </ul>
279 |
280 | Results in something like this:
281 |
282 | .. sourcecode:: html
283 |
284 | <ul class="my_list" id="list-42">
285 | ...
286 | </ul>
287 |
288 | As you can see it automatically prepends a space in front of the item
289 | if the filter returned something unless the second parameter is false.
290 |
291 | .. versionchanged:: 3.1.4
292 | Keys with ``/`` solidus, ``>`` greater-than sign, or ``=`` equals sign
293 | are not allowed.
294 |
295 | .. versionchanged:: 3.1.3
296 | Keys with spaces are not allowed.
297 | """
298 | items = []
299 |
300 | for key, value in d.items():
301 | if value is None or isinstance(value, Undefined):
302 | continue
303 |
304 | if _attr_key_re.search(key) is not None:
305 | raise ValueError(f"Invalid character in attribute name: {key!r}")
306 |
307 | items.append(f'{escape(key)}="{escape(value)}"')
308 |
309 | rv = " ".join(items)
310 |
311 | if autospace and rv:
312 | rv = " " + rv
313 |
314 | if eval_ctx.autoescape:
315 | rv = Markup(rv)
316 |
317 | return rv
318 |
319 |
320 | def do_capitalize(s: str) -> str:
321 | """Capitalize a value. The first character will be uppercase, all others
322 | lowercase.
323 | """
324 | return soft_str(s).capitalize()
325 |
326 |
327 | _word_beginning_split_re = re.compile(r"([-\s({\[<]+)")
328 |
329 |
330 | def do_title(s: str) -> str:
331 | """Return a titlecased version of the value. I.e. words will start with
332 | uppercase letters, all remaining characters are lowercase.
333 | """
334 | return "".join(
335 | [
336 | item[0].upper() + item[1:].lower()
337 | for item in _word_beginning_split_re.split(soft_str(s))
338 | if item
339 | ]
340 | )
341 |
342 |
343 | def do_dictsort(
344 | value: t.Mapping[K, V],
345 | case_sensitive: bool = False,
346 | by: 'te.Literal["key", "value"]' = "key",
347 | reverse: bool = False,
348 | ) -> t.List[t.Tuple[K, V]]:
349 | """Sort a dict and yield (key, value) pairs. Python dicts may not
350 | be in the order you want to display them in, so sort them first.
351 |
352 | .. sourcecode:: jinja
353 |
354 | {% for key, value in mydict|dictsort %}
355 | sort the dict by key, case insensitive
356 |
357 | {% for key, value in mydict|dictsort(reverse=true) %}
358 | sort the dict by key, case insensitive, reverse order
359 |
360 | {% for key, value in mydict|dictsort(true) %}
361 | sort the dict by key, case sensitive
362 |
363 | {% for key, value in mydict|dictsort(false, 'value') %}
364 | sort the dict by value, case insensitive
365 | """
366 | if by == "key":
367 | pos = 0
368 | elif by == "value":
369 | pos = 1
370 | else:
371 | raise FilterArgumentError('You can only sort by either "key" or "value"')
372 |
373 | def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
374 | value = item[pos]
375 |
376 | if not case_sensitive:
377 | value = ignore_case(value)
378 |
379 | return value
380 |
381 | return sorted(value.items(), key=sort_func, reverse=reverse)
382 |
383 |
384 | @pass_environment
385 | def do_sort(
386 | environment: "Environment",
387 | value: "t.Iterable[V]",
388 | reverse: bool = False,
389 | case_sensitive: bool = False,
390 | attribute: t.Optional[t.Union[str, int]] = None,
391 | ) -> "t.List[V]":
392 | """Sort an iterable using Python's :func:`sorted`.
393 |
394 | .. sourcecode:: jinja
395 |
396 | {% for city in cities|sort %}
397 | ...
398 | {% endfor %}
399 |
400 | :param reverse: Sort descending instead of ascending.
401 | :param case_sensitive: When sorting strings, sort upper and lower
402 | case separately.
403 | :param attribute: When sorting objects or dicts, an attribute or
404 | key to sort by. Can use dot notation like ``"address.city"``.
405 | Can be a list of attributes like ``"age,name"``.
406 |
407 | The sort is stable, it does not change the relative order of
408 | elements that compare equal. This makes it is possible to chain
409 | sorts on different attributes and ordering.
410 |
411 | .. sourcecode:: jinja
412 |
413 | {% for user in users|sort(attribute="name")
414 | |sort(reverse=true, attribute="age") %}
415 | ...
416 | {% endfor %}
417 |
418 | As a shortcut to chaining when the direction is the same for all
419 | attributes, pass a comma separate list of attributes.
420 |
421 | .. sourcecode:: jinja
422 |
423 | {% for user in users|sort(attribute="age,name") %}
424 | ...
425 | {% endfor %}
426 |
427 | .. versionchanged:: 2.11.0
428 | The ``attribute`` parameter can be a comma separated list of
429 | attributes, e.g. ``"age,name"``.
430 |
431 | .. versionchanged:: 2.6
432 | The ``attribute`` parameter was added.
433 | """
434 | key_func = make_multi_attrgetter(
435 | environment, attribute, postprocess=ignore_case if not case_sensitive else None
436 | )
437 | return sorted(value, key=key_func, reverse=reverse)
438 |
439 |
440 | @pass_environment
441 | def do_unique(
442 | environment: "Environment",
443 | value: "t.Iterable[V]",
444 | case_sensitive: bool = False,
445 | attribute: t.Optional[t.Union[str, int]] = None,
446 | ) -> "t.Iterator[V]":
447 | """Returns a list of unique items from the given iterable.
448 |
449 | .. sourcecode:: jinja
450 |
451 | {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }}
452 | -> ['foo', 'bar', 'foobar']
453 |
454 | The unique items are yielded in the same order as their first occurrence in
455 | the iterable passed to the filter.
456 |
457 | :param case_sensitive: Treat upper and lower case strings as distinct.
458 | :param attribute: Filter objects with unique values for this attribute.
459 | """
460 | getter = make_attrgetter(
461 | environment, attribute, postprocess=ignore_case if not case_sensitive else None
462 | )
463 | seen = set()
464 |
465 | for item in value:
466 | key = getter(item)
467 |
468 | if key not in seen:
469 | seen.add(key)
470 | yield item
471 |
472 |
473 | def _min_or_max(
474 | environment: "Environment",
475 | value: "t.Iterable[V]",
476 | func: "t.Callable[..., V]",
477 | case_sensitive: bool,
478 | attribute: t.Optional[t.Union[str, int]],
479 | ) -> "t.Union[V, Undefined]":
480 | it = iter(value)
481 |
482 | try:
483 | first = next(it)
484 | except StopIteration:
485 | return environment.undefined("No aggregated item, sequence was empty.")
486 |
487 | key_func = make_attrgetter(
488 | environment, attribute, postprocess=ignore_case if not case_sensitive else None
489 | )
490 | return func(chain([first], it), key=key_func)
491 |
492 |
493 | @pass_environment
494 | def do_min(
495 | environment: "Environment",
496 | value: "t.Iterable[V]",
497 | case_sensitive: bool = False,
498 | attribute: t.Optional[t.Union[str, int]] = None,
499 | ) -> "t.Union[V, Undefined]":
500 | """Return the smallest item from the sequence.
501 |
502 | .. sourcecode:: jinja
503 |
504 | {{ [1, 2, 3]|min }}
505 | -> 1
506 |
507 | :param case_sensitive: Treat upper and lower case strings as distinct.
508 | :param attribute: Get the object with the min value of this attribute.
509 | """
510 | return _min_or_max(environment, value, min, case_sensitive, attribute)
511 |
512 |
513 | @pass_environment
514 | def do_max(
515 | environment: "Environment",
516 | value: "t.Iterable[V]",
517 | case_sensitive: bool = False,
518 | attribute: t.Optional[t.Union[str, int]] = None,
519 | ) -> "t.Union[V, Undefined]":
520 | """Return the largest item from the sequence.
521 |
522 | .. sourcecode:: jinja
523 |
524 | {{ [1, 2, 3]|max }}
525 | -> 3
526 |
527 | :param case_sensitive: Treat upper and lower case strings as distinct.
528 | :param attribute: Get the object with the max value of this attribute.
529 | """
530 | return _min_or_max(environment, value, max, case_sensitive, attribute)
531 |
532 |
533 | def do_default(
534 | value: V,
535 | default_value: V = "", # type: ignore
536 | boolean: bool = False,
537 | ) -> V:
538 | """If the value is undefined it will return the passed default value,
539 | otherwise the value of the variable:
540 |
541 | .. sourcecode:: jinja
542 |
543 | {{ my_variable|default('my_variable is not defined') }}
544 |
545 | This will output the value of ``my_variable`` if the variable was
546 | defined, otherwise ``'my_variable is not defined'``. If you want
547 | to use default with variables that evaluate to false you have to
548 | set the second parameter to `true`:
549 |
550 | .. sourcecode:: jinja
551 |
552 | {{ ''|default('the string was empty', true) }}
553 |
554 | .. versionchanged:: 2.11
555 | It's now possible to configure the :class:`~jinja2.Environment` with
556 | :class:`~jinja2.ChainableUndefined` to make the `default` filter work
557 | on nested elements and attributes that may contain undefined values
558 | in the chain without getting an :exc:`~jinja2.UndefinedError`.
559 | """
560 | if isinstance(value, Undefined) or (boolean and not value):
561 | return default_value
562 |
563 | return value
564 |
565 |
566 | @pass_eval_context
567 | def sync_do_join(
568 | eval_ctx: "EvalContext",
569 | value: t.Iterable[t.Any],
570 | d: str = "",
571 | attribute: t.Optional[t.Union[str, int]] = None,
572 | ) -> str:
573 | """Return a string which is the concatenation of the strings in the
574 | sequence. The separator between elements is an empty string per
575 | default, you can define it with the optional parameter:
576 |
577 | .. sourcecode:: jinja
578 |
579 | {{ [1, 2, 3]|join('|') }}
580 | -> 1|2|3
581 |
582 | {{ [1, 2, 3]|join }}
583 | -> 123
584 |
585 | It is also possible to join certain attributes of an object:
586 |
587 | .. sourcecode:: jinja
588 |
589 | {{ users|join(', ', attribute='username') }}
590 |
591 | .. versionadded:: 2.6
592 | The `attribute` parameter was added.
593 | """
594 | if attribute is not None:
595 | value = map(make_attrgetter(eval_ctx.environment, attribute), value)
596 |
597 | # no automatic escaping? joining is a lot easier then
598 | if not eval_ctx.autoescape:
599 | return str(d).join(map(str, value))
600 |
601 | # if the delimiter doesn't have an html representation we check
602 | # if any of the items has. If yes we do a coercion to Markup
603 | if not hasattr(d, "__html__"):
604 | value = list(value)
605 | do_escape = False
606 |
607 | for idx, item in enumerate(value):
608 | if hasattr(item, "__html__"):
609 | do_escape = True
610 | else:
611 | value[idx] = str(item)
612 |
613 | if do_escape:
614 | d = escape(d)
615 | else:
616 | d = str(d)
617 |
618 | return d.join(value)
619 |
620 | # no html involved, to normal joining
621 | return soft_str(d).join(map(soft_str, value))
622 |
623 |
624 | @async_variant(sync_do_join) # type: ignore
625 | async def do_join(
626 | eval_ctx: "EvalContext",
627 | value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
628 | d: str = "",
629 | attribute: t.Optional[t.Union[str, int]] = None,
630 | ) -> str:
631 | return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute)
632 |
633 |
634 | def do_center(value: str, width: int = 80) -> str:
635 | """Centers the value in a field of a given width."""
636 | return soft_str(value).center(width)
637 |
638 |
639 | @pass_environment
640 | def sync_do_first(
641 | environment: "Environment", seq: "t.Iterable[V]"
642 | ) -> "t.Union[V, Undefined]":
643 | """Return the first item of a sequence."""
644 | try:
645 | return next(iter(seq))
646 | except StopIteration:
647 | return environment.undefined("No first item, sequence was empty.")
648 |
649 |
650 | @async_variant(sync_do_first) # type: ignore
651 | async def do_first(
652 | environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]"
653 | ) -> "t.Union[V, Undefined]":
654 | try:
655 | return await auto_aiter(seq).__anext__()
656 | except StopAsyncIteration:
657 | return environment.undefined("No first item, sequence was empty.")
658 |
659 |
660 | @pass_environment
661 | def do_last(
662 | environment: "Environment", seq: "t.Reversible[V]"
663 | ) -> "t.Union[V, Undefined]":
664 | """Return the last item of a sequence.
665 |
666 | Note: Does not work with generators. You may want to explicitly
667 | convert it to a list:
668 |
669 | .. sourcecode:: jinja
670 |
671 | {{ data | selectattr('name', '==', 'Jinja') | list | last }}
672 | """
673 | try:
674 | return next(iter(reversed(seq)))
675 | except StopIteration:
676 | return environment.undefined("No last item, sequence was empty.")
677 |
678 |
679 | # No async do_last, it may not be safe in async mode.
680 |
681 |
682 | @pass_context
683 | def do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]":
684 | """Return a random item from the sequence."""
685 | try:
686 | return random.choice(seq)
687 | except IndexError:
688 | return context.environment.undefined("No random item, sequence was empty.")
689 |
690 |
691 | def do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str:
692 | """Format the value like a 'human-readable' file size (i.e. 13 kB,
693 | 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
694 | Giga, etc.), if the second parameter is set to `True` the binary
695 | prefixes are used (Mebi, Gibi).
696 | """
697 | bytes = float(value)
698 | base = 1024 if binary else 1000
699 | prefixes = [
700 | ("KiB" if binary else "kB"),
701 | ("MiB" if binary else "MB"),
702 | ("GiB" if binary else "GB"),
703 | ("TiB" if binary else "TB"),
704 | ("PiB" if binary else "PB"),
705 | ("EiB" if binary else "EB"),
706 | ("ZiB" if binary else "ZB"),
707 | ("YiB" if binary else "YB"),
708 | ]
709 |
710 | if bytes == 1:
711 | return "1 Byte"
712 | elif bytes < base:
713 | return f"{int(bytes)} Bytes"
714 | else:
715 | for i, prefix in enumerate(prefixes):
716 | unit = base ** (i + 2)
717 |
718 | if bytes < unit:
719 | return f"{base * bytes / unit:.1f} {prefix}"
720 |
721 | return f"{base * bytes / unit:.1f} {prefix}"
722 |
723 |
724 | def do_pprint(value: t.Any) -> str:
725 | """Pretty print a variable. Useful for debugging."""
726 | return pformat(value)
727 |
728 |
729 | _uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$")
730 |
731 |
732 | @pass_eval_context
733 | def do_urlize(
734 | eval_ctx: "EvalContext",
735 | value: str,
736 | trim_url_limit: t.Optional[int] = None,
737 | nofollow: bool = False,
738 | target: t.Optional[str] = None,
739 | rel: t.Optional[str] = None,
740 | extra_schemes: t.Optional[t.Iterable[str]] = None,
741 | ) -> str:
742 | """Convert URLs in text into clickable links.
743 |
744 | This may not recognize links in some situations. Usually, a more
745 | comprehensive formatter, such as a Markdown library, is a better
746 | choice.
747 |
748 | Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email
749 | addresses. Links with trailing punctuation (periods, commas, closing
750 | parentheses) and leading punctuation (opening parentheses) are
751 | recognized excluding the punctuation. Email addresses that include
752 | header fields are not recognized (for example,
753 | ``mailto:[email protected][email protected]``).
754 |
755 | :param value: Original text containing URLs to link.
756 | :param trim_url_limit: Shorten displayed URL values to this length.
757 | :param nofollow: Add the ``rel=nofollow`` attribute to links.
758 | :param target: Add the ``target`` attribute to links.
759 | :param rel: Add the ``rel`` attribute to links.
760 | :param extra_schemes: Recognize URLs that start with these schemes
761 | in addition to the default behavior. Defaults to
762 | ``env.policies["urlize.extra_schemes"]``, which defaults to no
763 | extra schemes.
764 |
765 | .. versionchanged:: 3.0
766 | The ``extra_schemes`` parameter was added.
767 |
768 | .. versionchanged:: 3.0
769 | Generate ``https://`` links for URLs without a scheme.
770 |
771 | .. versionchanged:: 3.0
772 | The parsing rules were updated. Recognize email addresses with
773 | or without the ``mailto:`` scheme. Validate IP addresses. Ignore
774 | parentheses and brackets in more cases.
775 |
776 | .. versionchanged:: 2.8
777 | The ``target`` parameter was added.
778 | """
779 | policies = eval_ctx.environment.policies
780 | rel_parts = set((rel or "").split())
781 |
782 | if nofollow:
783 | rel_parts.add("nofollow")
784 |
785 | rel_parts.update((policies["urlize.rel"] or "").split())
786 | rel = " ".join(sorted(rel_parts)) or None
787 |
788 | if target is None:
789 | target = policies["urlize.target"]
790 |
791 | if extra_schemes is None:
792 | extra_schemes = policies["urlize.extra_schemes"] or ()
793 |
794 | for scheme in extra_schemes:
795 | if _uri_scheme_re.fullmatch(scheme) is None:
796 | raise FilterArgumentError(f"{scheme!r} is not a valid URI scheme prefix.")
797 |
798 | rv = urlize(
799 | value,
800 | trim_url_limit=trim_url_limit,
801 | rel=rel,
802 | target=target,
803 | extra_schemes=extra_schemes,
804 | )
805 |
806 | if eval_ctx.autoescape:
807 | rv = Markup(rv)
808 |
809 | return rv
810 |
811 |
812 | def do_indent(
813 | s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False
814 | ) -> str:
815 | """Return a copy of the string with each line indented by 4 spaces. The
816 | first line and blank lines are not indented by default.
817 |
818 | :param width: Number of spaces, or a string, to indent by.
819 | :param first: Don't skip indenting the first line.
820 | :param blank: Don't skip indenting empty lines.
821 |
822 | .. versionchanged:: 3.0
823 | ``width`` can be a string.
824 |
825 | .. versionchanged:: 2.10
826 | Blank lines are not indented by default.
827 |
828 | Rename the ``indentfirst`` argument to ``first``.
829 | """
830 | if isinstance(width, str):
831 | indention = width
832 | else:
833 | indention = " " * width
834 |
835 | newline = "\n"
836 |
837 | if isinstance(s, Markup):
838 | indention = Markup(indention)
839 | newline = Markup(newline)
840 |
841 | s += newline # this quirk is necessary for splitlines method
842 |
843 | if blank:
844 | rv = (newline + indention).join(s.splitlines())
845 | else:
846 | lines = s.splitlines()
847 | rv = lines.pop(0)
848 |
849 | if lines:
850 | rv += newline + newline.join(
851 | indention + line if line else line for line in lines
852 | )
853 |
854 | if first:
855 | rv = indention + rv
856 |
857 | return rv
858 |
859 |
860 | @pass_environment
861 | def do_truncate(
862 | env: "Environment",
863 | s: str,
864 | length: int = 255,
865 | killwords: bool = False,
866 | end: str = "...",
867 | leeway: t.Optional[int] = None,
868 | ) -> str:
869 | """Return a truncated copy of the string. The length is specified
870 | with the first parameter which defaults to ``255``. If the second
871 | parameter is ``true`` the filter will cut the text at length. Otherwise
872 | it will discard the last word. If the text was in fact
873 | truncated it will append an ellipsis sign (``"..."``). If you want a
874 | different ellipsis sign than ``"..."`` you can specify it using the
875 | third parameter. Strings that only exceed the length by the tolerance
876 | margin given in the fourth parameter will not be truncated.
877 |
878 | .. sourcecode:: jinja
879 |
880 | {{ "foo bar baz qux"|truncate(9) }}
881 | -> "foo..."
882 | {{ "foo bar baz qux"|truncate(9, True) }}
883 | -> "foo ba..."
884 | {{ "foo bar baz qux"|truncate(11) }}
885 | -> "foo bar baz qux"
886 | {{ "foo bar baz qux"|truncate(11, False, '...', 0) }}
887 | -> "foo bar..."
888 |
889 | The default leeway on newer Jinja versions is 5 and was 0 before but
890 | can be reconfigured globally.
891 | """
892 | if leeway is None:
893 | leeway = env.policies["truncate.leeway"]
894 |
895 | assert length >= len(end), f"expected length >= {len(end)}, got {length}"
896 | assert leeway >= 0, f"expected leeway >= 0, got {leeway}"
897 |
898 | if len(s) <= length + leeway:
899 | return s
900 |
901 | if killwords:
902 | return s[: length - len(end)] + end
903 |
904 | result = s[: length - len(end)].rsplit(" ", 1)[0]
905 | return result + end
906 |
907 |
908 | @pass_environment
909 | def do_wordwrap(
910 | environment: "Environment",
911 | s: str,
912 | width: int = 79,
913 | break_long_words: bool = True,
914 | wrapstring: t.Optional[str] = None,
915 | break_on_hyphens: bool = True,
916 | ) -> str:
917 | """Wrap a string to the given width. Existing newlines are treated
918 | as paragraphs to be wrapped separately.
919 |
920 | :param s: Original text to wrap.
921 | :param width: Maximum length of wrapped lines.
922 | :param break_long_words: If a word is longer than ``width``, break
923 | it across lines.
924 | :param break_on_hyphens: If a word contains hyphens, it may be split
925 | across lines.
926 | :param wrapstring: String to join each wrapped line. Defaults to
927 | :attr:`Environment.newline_sequence`.
928 |
929 | .. versionchanged:: 2.11
930 | Existing newlines are treated as paragraphs wrapped separately.
931 |
932 | .. versionchanged:: 2.11
933 | Added the ``break_on_hyphens`` parameter.
934 |
935 | .. versionchanged:: 2.7
936 | Added the ``wrapstring`` parameter.
937 | """
938 | import textwrap
939 |
940 | if wrapstring is None:
941 | wrapstring = environment.newline_sequence
942 |
943 | # textwrap.wrap doesn't consider existing newlines when wrapping.
944 | # If the string has a newline before width, wrap will still insert
945 | # a newline at width, resulting in a short line. Instead, split and
946 | # wrap each paragraph individually.
947 | return wrapstring.join(
948 | [
949 | wrapstring.join(
950 | textwrap.wrap(
951 | line,
952 | width=width,
953 | expand_tabs=False,
954 | replace_whitespace=False,
955 | break_long_words=break_long_words,
956 | break_on_hyphens=break_on_hyphens,
957 | )
958 | )
959 | for line in s.splitlines()
960 | ]
961 | )
962 |
963 |
964 | _word_re = re.compile(r"\w+")
965 |
966 |
967 | def do_wordcount(s: str) -> int:
968 | """Count the words in that string."""
969 | return len(_word_re.findall(soft_str(s)))
970 |
971 |
972 | def do_int(value: t.Any, default: int = 0, base: int = 10) -> int:
973 | """Convert the value into an integer. If the
974 | conversion doesn't work it will return ``0``. You can
975 | override this default using the first parameter. You
976 | can also override the default base (10) in the second
977 | parameter, which handles input with prefixes such as
978 | 0b, 0o and 0x for bases 2, 8 and 16 respectively.
979 | The base is ignored for decimal numbers and non-string values.
980 | """
981 | try:
982 | if isinstance(value, str):
983 | return int(value, base)
984 |
985 | return int(value)
986 | except (TypeError, ValueError):
987 | # this quirk is necessary so that "42.23"|int gives 42.
988 | try:
989 | return int(float(value))
990 | except (TypeError, ValueError):
991 | return default
992 |
993 |
994 | def do_float(value: t.Any, default: float = 0.0) -> float:
995 | """Convert the value into a floating point number. If the
996 | conversion doesn't work it will return ``0.0``. You can
997 | override this default using the first parameter.
998 | """
999 | try:
1000 | return float(value)
1001 | except (TypeError, ValueError):
1002 | return default
1003 |
1004 |
1005 | def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str:
1006 | """Apply the given values to a `printf-style`_ format string, like
1007 | ``string % values``.
1008 |
1009 | .. sourcecode:: jinja
1010 |
1011 | {{ "%s, %s!"|format(greeting, name) }}
1012 | Hello, World!
1013 |
1014 | In most cases it should be more convenient and efficient to use the
1015 | ``%`` operator or :meth:`str.format`.
1016 |
1017 | .. code-block:: text
1018 |
1019 | {{ "%s, %s!" % (greeting, name) }}
1020 | {{ "{}, {}!".format(greeting, name) }}
1021 |
1022 | .. _printf-style: https://docs.python.org/library/stdtypes.html
1023 | #printf-style-string-formatting
1024 | """
1025 | if args and kwargs:
1026 | raise FilterArgumentError(
1027 | "can't handle positional and keyword arguments at the same time"
1028 | )
1029 |
1030 | return soft_str(value) % (kwargs or args)
1031 |
1032 |
1033 | def do_trim(value: str, chars: t.Optional[str] = None) -> str:
1034 | """Strip leading and trailing characters, by default whitespace."""
1035 | return soft_str(value).strip(chars)
1036 |
1037 |
1038 | def do_striptags(value: "t.Union[str, HasHTML]") -> str:
1039 | """Strip SGML/XML tags and replace adjacent whitespace by one space."""
1040 | if hasattr(value, "__html__"):
1041 | value = t.cast("HasHTML", value).__html__()
1042 |
1043 | return Markup(str(value)).striptags()
1044 |
1045 |
1046 | def sync_do_slice(
1047 | value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None
1048 | ) -> "t.Iterator[t.List[V]]":
1049 | """Slice an iterator and return a list of lists containing
1050 | those items. Useful if you want to create a div containing
1051 | three ul tags that represent columns:
1052 |
1053 | .. sourcecode:: html+jinja
1054 |
1055 | <div class="columnwrapper">
1056 | {%- for column in items|slice(3) %}
1057 | <ul class="column-{{ loop.index }}">
1058 | {%- for item in column %}
1059 | <li>{{ item }}</li>
1060 | {%- endfor %}
1061 | </ul>
1062 | {%- endfor %}
1063 | </div>
1064 |
1065 | If you pass it a second argument it's used to fill missing
1066 | values on the last iteration.
1067 | """
1068 | seq = list(value)
1069 | length = len(seq)
1070 | items_per_slice = length // slices
1071 | slices_with_extra = length % slices
1072 | offset = 0
1073 |
1074 | for slice_number in range(slices):
1075 | start = offset + slice_number * items_per_slice
1076 |
1077 | if slice_number < slices_with_extra:
1078 | offset += 1
1079 |
1080 | end = offset + (slice_number + 1) * items_per_slice
1081 | tmp = seq[start:end]
1082 |
1083 | if fill_with is not None and slice_number >= slices_with_extra:
1084 | tmp.append(fill_with)
1085 |
1086 | yield tmp
1087 |
1088 |
1089 | @async_variant(sync_do_slice) # type: ignore
1090 | async def do_slice(
1091 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1092 | slices: int,
1093 | fill_with: t.Optional[t.Any] = None,
1094 | ) -> "t.Iterator[t.List[V]]":
1095 | return sync_do_slice(await auto_to_list(value), slices, fill_with)
1096 |
1097 |
1098 | def do_batch(
1099 | value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None
1100 | ) -> "t.Iterator[t.List[V]]":
1101 | """
1102 | A filter that batches items. It works pretty much like `slice`
1103 | just the other way round. It returns a list of lists with the
1104 | given number of items. If you provide a second parameter this
1105 | is used to fill up missing items. See this example:
1106 |
1107 | .. sourcecode:: html+jinja
1108 |
1109 | <table>
1110 | {%- for row in items|batch(3, ' ') %}
1111 | <tr>
1112 | {%- for column in row %}
1113 | <td>{{ column }}</td>
1114 | {%- endfor %}
1115 | </tr>
1116 | {%- endfor %}
1117 | </table>
1118 | """
1119 | tmp: "t.List[V]" = []
1120 |
1121 | for item in value:
1122 | if len(tmp) == linecount:
1123 | yield tmp
1124 | tmp = []
1125 |
1126 | tmp.append(item)
1127 |
1128 | if tmp:
1129 | if fill_with is not None and len(tmp) < linecount:
1130 | tmp += [fill_with] * (linecount - len(tmp))
1131 |
1132 | yield tmp
1133 |
1134 |
1135 | def do_round(
1136 | value: float,
1137 | precision: int = 0,
1138 | method: 'te.Literal["common", "ceil", "floor"]' = "common",
1139 | ) -> float:
1140 | """Round the number to a given precision. The first
1141 | parameter specifies the precision (default is ``0``), the
1142 | second the rounding method:
1143 |
1144 | - ``'common'`` rounds either up or down
1145 | - ``'ceil'`` always rounds up
1146 | - ``'floor'`` always rounds down
1147 |
1148 | If you don't specify a method ``'common'`` is used.
1149 |
1150 | .. sourcecode:: jinja
1151 |
1152 | {{ 42.55|round }}
1153 | -> 43.0
1154 | {{ 42.55|round(1, 'floor') }}
1155 | -> 42.5
1156 |
1157 | Note that even if rounded to 0 precision, a float is returned. If
1158 | you need a real integer, pipe it through `int`:
1159 |
1160 | .. sourcecode:: jinja
1161 |
1162 | {{ 42.55|round|int }}
1163 | -> 43
1164 | """
1165 | if method not in {"common", "ceil", "floor"}:
1166 | raise FilterArgumentError("method must be common, ceil or floor")
1167 |
1168 | if method == "common":
1169 | return round(value, precision)
1170 |
1171 | func = getattr(math, method)
1172 | return t.cast(float, func(value * (10**precision)) / (10**precision))
1173 |
1174 |
1175 | class _GroupTuple(t.NamedTuple):
1176 | grouper: t.Any
1177 | list: t.List[t.Any]
1178 |
1179 | # Use the regular tuple repr to hide this subclass if users print
1180 | # out the value during debugging.
1181 | def __repr__(self) -> str:
1182 | return tuple.__repr__(self)
1183 |
1184 | def __str__(self) -> str:
1185 | return tuple.__str__(self)
1186 |
1187 |
1188 | @pass_environment
1189 | def sync_do_groupby(
1190 | environment: "Environment",
1191 | value: "t.Iterable[V]",
1192 | attribute: t.Union[str, int],
1193 | default: t.Optional[t.Any] = None,
1194 | case_sensitive: bool = False,
1195 | ) -> "t.List[_GroupTuple]":
1196 | """Group a sequence of objects by an attribute using Python's
1197 | :func:`itertools.groupby`. The attribute can use dot notation for
1198 | nested access, like ``"address.city"``. Unlike Python's ``groupby``,
1199 | the values are sorted first so only one group is returned for each
1200 | unique value.
1201 |
1202 | For example, a list of ``User`` objects with a ``city`` attribute
1203 | can be rendered in groups. In this example, ``grouper`` refers to
1204 | the ``city`` value of the group.
1205 |
1206 | .. sourcecode:: html+jinja
1207 |
1208 | <ul>{% for city, items in users|groupby("city") %}
1209 | <li>{{ city }}
1210 | <ul>{% for user in items %}
1211 | <li>{{ user.name }}
1212 | {% endfor %}</ul>
1213 | </li>
1214 | {% endfor %}</ul>
1215 |
1216 | ``groupby`` yields namedtuples of ``(grouper, list)``, which
1217 | can be used instead of the tuple unpacking above. ``grouper`` is the
1218 | value of the attribute, and ``list`` is the items with that value.
1219 |
1220 | .. sourcecode:: html+jinja
1221 |
1222 | <ul>{% for group in users|groupby("city") %}
1223 | <li>{{ group.grouper }}: {{ group.list|join(", ") }}
1224 | {% endfor %}</ul>
1225 |
1226 | You can specify a ``default`` value to use if an object in the list
1227 | does not have the given attribute.
1228 |
1229 | .. sourcecode:: jinja
1230 |
1231 | <ul>{% for city, items in users|groupby("city", default="NY") %}
1232 | <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li>
1233 | {% endfor %}</ul>
1234 |
1235 | Like the :func:`~jinja-filters.sort` filter, sorting and grouping is
1236 | case-insensitive by default. The ``key`` for each group will have
1237 | the case of the first item in that group of values. For example, if
1238 | a list of users has cities ``["CA", "NY", "ca"]``, the "CA" group
1239 | will have two values. This can be disabled by passing
1240 | ``case_sensitive=True``.
1241 |
1242 | .. versionchanged:: 3.1
1243 | Added the ``case_sensitive`` parameter. Sorting and grouping is
1244 | case-insensitive by default, matching other filters that do
1245 | comparisons.
1246 |
1247 | .. versionchanged:: 3.0
1248 | Added the ``default`` parameter.
1249 |
1250 | .. versionchanged:: 2.6
1251 | The attribute supports dot notation for nested access.
1252 | """
1253 | expr = make_attrgetter(
1254 | environment,
1255 | attribute,
1256 | postprocess=ignore_case if not case_sensitive else None,
1257 | default=default,
1258 | )
1259 | out = [
1260 | _GroupTuple(key, list(values))
1261 | for key, values in groupby(sorted(value, key=expr), expr)
1262 | ]
1263 |
1264 | if not case_sensitive:
1265 | # Return the real key from the first value instead of the lowercase key.
1266 | output_expr = make_attrgetter(environment, attribute, default=default)
1267 | out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
1268 |
1269 | return out
1270 |
1271 |
1272 | @async_variant(sync_do_groupby) # type: ignore
1273 | async def do_groupby(
1274 | environment: "Environment",
1275 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1276 | attribute: t.Union[str, int],
1277 | default: t.Optional[t.Any] = None,
1278 | case_sensitive: bool = False,
1279 | ) -> "t.List[_GroupTuple]":
1280 | expr = make_attrgetter(
1281 | environment,
1282 | attribute,
1283 | postprocess=ignore_case if not case_sensitive else None,
1284 | default=default,
1285 | )
1286 | out = [
1287 | _GroupTuple(key, await auto_to_list(values))
1288 | for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr)
1289 | ]
1290 |
1291 | if not case_sensitive:
1292 | # Return the real key from the first value instead of the lowercase key.
1293 | output_expr = make_attrgetter(environment, attribute, default=default)
1294 | out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
1295 |
1296 | return out
1297 |
1298 |
1299 | @pass_environment
1300 | def sync_do_sum(
1301 | environment: "Environment",
1302 | iterable: "t.Iterable[V]",
1303 | attribute: t.Optional[t.Union[str, int]] = None,
1304 | start: V = 0, # type: ignore
1305 | ) -> V:
1306 | """Returns the sum of a sequence of numbers plus the value of parameter
1307 | 'start' (which defaults to 0). When the sequence is empty it returns
1308 | start.
1309 |
1310 | It is also possible to sum up only certain attributes:
1311 |
1312 | .. sourcecode:: jinja
1313 |
1314 | Total: {{ items|sum(attribute='price') }}
1315 |
1316 | .. versionchanged:: 2.6
1317 | The ``attribute`` parameter was added to allow summing up over
1318 | attributes. Also the ``start`` parameter was moved on to the right.
1319 | """
1320 | if attribute is not None:
1321 | iterable = map(make_attrgetter(environment, attribute), iterable)
1322 |
1323 | return sum(iterable, start) # type: ignore[no-any-return, call-overload]
1324 |
1325 |
1326 | @async_variant(sync_do_sum) # type: ignore
1327 | async def do_sum(
1328 | environment: "Environment",
1329 | iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1330 | attribute: t.Optional[t.Union[str, int]] = None,
1331 | start: V = 0, # type: ignore
1332 | ) -> V:
1333 | rv = start
1334 |
1335 | if attribute is not None:
1336 | func = make_attrgetter(environment, attribute)
1337 | else:
1338 |
1339 | def func(x: V) -> V:
1340 | return x
1341 |
1342 | async for item in auto_aiter(iterable):
1343 | rv += func(item)
1344 |
1345 | return rv
1346 |
1347 |
1348 | def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
1349 | """Convert the value into a list. If it was a string the returned list
1350 | will be a list of characters.
1351 | """
1352 | return list(value)
1353 |
1354 |
1355 | @async_variant(sync_do_list) # type: ignore
1356 | async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
1357 | return await auto_to_list(value)
1358 |
1359 |
1360 | def do_mark_safe(value: str) -> Markup:
1361 | """Mark the value as safe which means that in an environment with automatic
1362 | escaping enabled this variable will not be escaped.
1363 | """
1364 | return Markup(value)
1365 |
1366 |
1367 | def do_mark_unsafe(value: str) -> str:
1368 | """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
1369 | return str(value)
1370 |
1371 |
1372 | @typing.overload
1373 | def do_reverse(value: str) -> str: ...
1374 |
1375 |
1376 | @typing.overload
1377 | def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]": ...
1378 |
1379 |
1380 | def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]:
1381 | """Reverse the object or return an iterator that iterates over it the other
1382 | way round.
1383 | """
1384 | if isinstance(value, str):
1385 | return value[::-1]
1386 |
1387 | try:
1388 | return reversed(value) # type: ignore
1389 | except TypeError:
1390 | try:
1391 | rv = list(value)
1392 | rv.reverse()
1393 | return rv
1394 | except TypeError as e:
1395 | raise FilterArgumentError("argument must be iterable") from e
1396 |
1397 |
1398 | @pass_environment
1399 | def do_attr(
1400 | environment: "Environment", obj: t.Any, name: str
1401 | ) -> t.Union[Undefined, t.Any]:
1402 | """Get an attribute of an object. ``foo|attr("bar")`` works like
1403 | ``foo.bar`` just that always an attribute is returned and items are not
1404 | looked up.
1405 |
1406 | See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
1407 | """
1408 | try:
1409 | name = str(name)
1410 | except UnicodeError:
1411 | pass
1412 | else:
1413 | try:
1414 | value = getattr(obj, name)
1415 | except AttributeError:
1416 | pass
1417 | else:
1418 | if environment.sandboxed:
1419 | environment = t.cast("SandboxedEnvironment", environment)
1420 |
1421 | if not environment.is_safe_attribute(obj, name, value):
1422 | return environment.unsafe_undefined(obj, name)
1423 |
1424 | return value
1425 |
1426 | return environment.undefined(obj=obj, name=name)
1427 |
1428 |
1429 | @typing.overload
1430 | def sync_do_map(
1431 | context: "Context",
1432 | value: t.Iterable[t.Any],
1433 | name: str,
1434 | *args: t.Any,
1435 | **kwargs: t.Any,
1436 | ) -> t.Iterable[t.Any]: ...
1437 |
1438 |
1439 | @typing.overload
1440 | def sync_do_map(
1441 | context: "Context",
1442 | value: t.Iterable[t.Any],
1443 | *,
1444 | attribute: str = ...,
1445 | default: t.Optional[t.Any] = None,
1446 | ) -> t.Iterable[t.Any]: ...
1447 |
1448 |
1449 | @pass_context
1450 | def sync_do_map(
1451 | context: "Context", value: t.Iterable[t.Any], *args: t.Any, **kwargs: t.Any
1452 | ) -> t.Iterable[t.Any]:
1453 | """Applies a filter on a sequence of objects or looks up an attribute.
1454 | This is useful when dealing with lists of objects but you are really
1455 | only interested in a certain value of it.
1456 |
1457 | The basic usage is mapping on an attribute. Imagine you have a list
1458 | of users but you are only interested in a list of usernames:
1459 |
1460 | .. sourcecode:: jinja
1461 |
1462 | Users on this page: {{ users|map(attribute='username')|join(', ') }}
1463 |
1464 | You can specify a ``default`` value to use if an object in the list
1465 | does not have the given attribute.
1466 |
1467 | .. sourcecode:: jinja
1468 |
1469 | {{ users|map(attribute="username", default="Anonymous")|join(", ") }}
1470 |
1471 | Alternatively you can let it invoke a filter by passing the name of the
1472 | filter and the arguments afterwards. A good example would be applying a
1473 | text conversion filter on a sequence:
1474 |
1475 | .. sourcecode:: jinja
1476 |
1477 | Users on this page: {{ titles|map('lower')|join(', ') }}
1478 |
1479 | Similar to a generator comprehension such as:
1480 |
1481 | .. code-block:: python
1482 |
1483 | (u.username for u in users)
1484 | (getattr(u, "username", "Anonymous") for u in users)
1485 | (do_lower(x) for x in titles)
1486 |
1487 | .. versionchanged:: 2.11.0
1488 | Added the ``default`` parameter.
1489 |
1490 | .. versionadded:: 2.7
1491 | """
1492 | if value:
1493 | func = prepare_map(context, args, kwargs)
1494 |
1495 | for item in value:
1496 | yield func(item)
1497 |
1498 |
1499 | @typing.overload
1500 | def do_map(
1501 | context: "Context",
1502 | value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
1503 | name: str,
1504 | *args: t.Any,
1505 | **kwargs: t.Any,
1506 | ) -> t.Iterable[t.Any]: ...
1507 |
1508 |
1509 | @typing.overload
1510 | def do_map(
1511 | context: "Context",
1512 | value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
1513 | *,
1514 | attribute: str = ...,
1515 | default: t.Optional[t.Any] = None,
1516 | ) -> t.Iterable[t.Any]: ...
1517 |
1518 |
1519 | @async_variant(sync_do_map) # type: ignore
1520 | async def do_map(
1521 | context: "Context",
1522 | value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
1523 | *args: t.Any,
1524 | **kwargs: t.Any,
1525 | ) -> t.AsyncIterable[t.Any]:
1526 | if value:
1527 | func = prepare_map(context, args, kwargs)
1528 |
1529 | async for item in auto_aiter(value):
1530 | yield await auto_await(func(item))
1531 |
1532 |
1533 | @pass_context
1534 | def sync_do_select(
1535 | context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1536 | ) -> "t.Iterator[V]":
1537 | """Filters a sequence of objects by applying a test to each object,
1538 | and only selecting the objects with the test succeeding.
1539 |
1540 | If no test is specified, each object will be evaluated as a boolean.
1541 |
1542 | Example usage:
1543 |
1544 | .. sourcecode:: jinja
1545 |
1546 | {{ numbers|select("odd") }}
1547 | {{ numbers|select("odd") }}
1548 | {{ numbers|select("divisibleby", 3) }}
1549 | {{ numbers|select("lessthan", 42) }}
1550 | {{ strings|select("equalto", "mystring") }}
1551 |
1552 | Similar to a generator comprehension such as:
1553 |
1554 | .. code-block:: python
1555 |
1556 | (n for n in numbers if test_odd(n))
1557 | (n for n in numbers if test_divisibleby(n, 3))
1558 |
1559 | .. versionadded:: 2.7
1560 | """
1561 | return select_or_reject(context, value, args, kwargs, lambda x: x, False)
1562 |
1563 |
1564 | @async_variant(sync_do_select) # type: ignore
1565 | async def do_select(
1566 | context: "Context",
1567 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1568 | *args: t.Any,
1569 | **kwargs: t.Any,
1570 | ) -> "t.AsyncIterator[V]":
1571 | return async_select_or_reject(context, value, args, kwargs, lambda x: x, False)
1572 |
1573 |
1574 | @pass_context
1575 | def sync_do_reject(
1576 | context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1577 | ) -> "t.Iterator[V]":
1578 | """Filters a sequence of objects by applying a test to each object,
1579 | and rejecting the objects with the test succeeding.
1580 |
1581 | If no test is specified, each object will be evaluated as a boolean.
1582 |
1583 | Example usage:
1584 |
1585 | .. sourcecode:: jinja
1586 |
1587 | {{ numbers|reject("odd") }}
1588 |
1589 | Similar to a generator comprehension such as:
1590 |
1591 | .. code-block:: python
1592 |
1593 | (n for n in numbers if not test_odd(n))
1594 |
1595 | .. versionadded:: 2.7
1596 | """
1597 | return select_or_reject(context, value, args, kwargs, lambda x: not x, False)
1598 |
1599 |
1600 | @async_variant(sync_do_reject) # type: ignore
1601 | async def do_reject(
1602 | context: "Context",
1603 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1604 | *args: t.Any,
1605 | **kwargs: t.Any,
1606 | ) -> "t.AsyncIterator[V]":
1607 | return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False)
1608 |
1609 |
1610 | @pass_context
1611 | def sync_do_selectattr(
1612 | context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1613 | ) -> "t.Iterator[V]":
1614 | """Filters a sequence of objects by applying a test to the specified
1615 | attribute of each object, and only selecting the objects with the
1616 | test succeeding.
1617 |
1618 | If no test is specified, the attribute's value will be evaluated as
1619 | a boolean.
1620 |
1621 | Example usage:
1622 |
1623 | .. sourcecode:: jinja
1624 |
1625 | {{ users|selectattr("is_active") }}
1626 | {{ users|selectattr("email", "none") }}
1627 |
1628 | Similar to a generator comprehension such as:
1629 |
1630 | .. code-block:: python
1631 |
1632 | (u for user in users if user.is_active)
1633 | (u for user in users if test_none(user.email))
1634 |
1635 | .. versionadded:: 2.7
1636 | """
1637 | return select_or_reject(context, value, args, kwargs, lambda x: x, True)
1638 |
1639 |
1640 | @async_variant(sync_do_selectattr) # type: ignore
1641 | async def do_selectattr(
1642 | context: "Context",
1643 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1644 | *args: t.Any,
1645 | **kwargs: t.Any,
1646 | ) -> "t.AsyncIterator[V]":
1647 | return async_select_or_reject(context, value, args, kwargs, lambda x: x, True)
1648 |
1649 |
1650 | @pass_context
1651 | def sync_do_rejectattr(
1652 | context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1653 | ) -> "t.Iterator[V]":
1654 | """Filters a sequence of objects by applying a test to the specified
1655 | attribute of each object, and rejecting the objects with the test
1656 | succeeding.
1657 |
1658 | If no test is specified, the attribute's value will be evaluated as
1659 | a boolean.
1660 |
1661 | .. sourcecode:: jinja
1662 |
1663 | {{ users|rejectattr("is_active") }}
1664 | {{ users|rejectattr("email", "none") }}
1665 |
1666 | Similar to a generator comprehension such as:
1667 |
1668 | .. code-block:: python
1669 |
1670 | (u for user in users if not user.is_active)
1671 | (u for user in users if not test_none(user.email))
1672 |
1673 | .. versionadded:: 2.7
1674 | """
1675 | return select_or_reject(context, value, args, kwargs, lambda x: not x, True)
1676 |
1677 |
1678 | @async_variant(sync_do_rejectattr) # type: ignore
1679 | async def do_rejectattr(
1680 | context: "Context",
1681 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1682 | *args: t.Any,
1683 | **kwargs: t.Any,
1684 | ) -> "t.AsyncIterator[V]":
1685 | return async_select_or_reject(context, value, args, kwargs, lambda x: not x, True)
1686 |
1687 |
1688 | @pass_eval_context
1689 | def do_tojson(
1690 | eval_ctx: "EvalContext", value: t.Any, indent: t.Optional[int] = None
1691 | ) -> Markup:
1692 | """Serialize an object to a string of JSON, and mark it safe to
1693 | render in HTML. This filter is only for use in HTML documents.
1694 |
1695 | The returned string is safe to render in HTML documents and
1696 | ``<script>`` tags. The exception is in HTML attributes that are
1697 | double quoted; either use single quotes or the ``|forceescape``
1698 | filter.
1699 |
1700 | :param value: The object to serialize to JSON.
1701 | :param indent: The ``indent`` parameter passed to ``dumps``, for
1702 | pretty-printing the value.
1703 |
1704 | .. versionadded:: 2.9
1705 | """
1706 | policies = eval_ctx.environment.policies
1707 | dumps = policies["json.dumps_function"]
1708 | kwargs = policies["json.dumps_kwargs"]
1709 |
1710 | if indent is not None:
1711 | kwargs = kwargs.copy()
1712 | kwargs["indent"] = indent
1713 |
1714 | return htmlsafe_json_dumps(value, dumps=dumps, **kwargs)
1715 |
1716 |
1717 | def prepare_map(
1718 | context: "Context", args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
1719 | ) -> t.Callable[[t.Any], t.Any]:
1720 | if not args and "attribute" in kwargs:
1721 | attribute = kwargs.pop("attribute")
1722 | default = kwargs.pop("default", None)
1723 |
1724 | if kwargs:
1725 | raise FilterArgumentError(
1726 | f"Unexpected keyword argument {next(iter(kwargs))!r}"
1727 | )
1728 |
1729 | func = make_attrgetter(context.environment, attribute, default=default)
1730 | else:
1731 | try:
1732 | name = args[0]
1733 | args = args[1:]
1734 | except LookupError:
1735 | raise FilterArgumentError("map requires a filter argument") from None
1736 |
1737 | def func(item: t.Any) -> t.Any:
1738 | return context.environment.call_filter(
1739 | name, item, args, kwargs, context=context
1740 | )
1741 |
1742 | return func
1743 |
1744 |
1745 | def prepare_select_or_reject(
1746 | context: "Context",
1747 | args: t.Tuple[t.Any, ...],
1748 | kwargs: t.Dict[str, t.Any],
1749 | modfunc: t.Callable[[t.Any], t.Any],
1750 | lookup_attr: bool,
1751 | ) -> t.Callable[[t.Any], t.Any]:
1752 | if lookup_attr:
1753 | try:
1754 | attr = args[0]
1755 | except LookupError:
1756 | raise FilterArgumentError("Missing parameter for attribute name") from None
1757 |
1758 | transfunc = make_attrgetter(context.environment, attr)
1759 | off = 1
1760 | else:
1761 | off = 0
1762 |
1763 | def transfunc(x: V) -> V:
1764 | return x
1765 |
1766 | try:
1767 | name = args[off]
1768 | args = args[1 + off :]
1769 |
1770 | def func(item: t.Any) -> t.Any:
1771 | return context.environment.call_test(name, item, args, kwargs)
1772 |
1773 | except LookupError:
1774 | func = bool # type: ignore
1775 |
1776 | return lambda item: modfunc(func(transfunc(item)))
1777 |
1778 |
1779 | def select_or_reject(
1780 | context: "Context",
1781 | value: "t.Iterable[V]",
1782 | args: t.Tuple[t.Any, ...],
1783 | kwargs: t.Dict[str, t.Any],
1784 | modfunc: t.Callable[[t.Any], t.Any],
1785 | lookup_attr: bool,
1786 | ) -> "t.Iterator[V]":
1787 | if value:
1788 | func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr)
1789 |
1790 | for item in value:
1791 | if func(item):
1792 | yield item
1793 |
1794 |
1795 | async def async_select_or_reject(
1796 | context: "Context",
1797 | value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1798 | args: t.Tuple[t.Any, ...],
1799 | kwargs: t.Dict[str, t.Any],
1800 | modfunc: t.Callable[[t.Any], t.Any],
1801 | lookup_attr: bool,
1802 | ) -> "t.AsyncIterator[V]":
1803 | if value:
1804 | func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr)
1805 |
1806 | async for item in auto_aiter(value):
1807 | if func(item):
1808 | yield item
1809 |
1810 |
1811 | FILTERS = {
1812 | "abs": abs,
1813 | "attr": do_attr,
1814 | "batch": do_batch,
1815 | "capitalize": do_capitalize,
1816 | "center": do_center,
1817 | "count": len,
1818 | "d": do_default,
1819 | "default": do_default,
1820 | "dictsort": do_dictsort,
1821 | "e": escape,
1822 | "escape": escape,
1823 | "filesizeformat": do_filesizeformat,
1824 | "first": do_first,
1825 | "float": do_float,
1826 | "forceescape": do_forceescape,
1827 | "format": do_format,
1828 | "groupby": do_groupby,
1829 | "indent": do_indent,
1830 | "int": do_int,
1831 | "join": do_join,
1832 | "last": do_last,
1833 | "length": len,
1834 | "list": do_list,
1835 | "lower": do_lower,
1836 | "items": do_items,
1837 | "map": do_map,
1838 | "min": do_min,
1839 | "max": do_max,
1840 | "pprint": do_pprint,
1841 | "random": do_random,
1842 | "reject": do_reject,
1843 | "rejectattr": do_rejectattr,
1844 | "replace": do_replace,
1845 | "reverse": do_reverse,
1846 | "round": do_round,
1847 | "safe": do_mark_safe,
1848 | "select": do_select,
1849 | "selectattr": do_selectattr,
1850 | "slice": do_slice,
1851 | "sort": do_sort,
1852 | "string": soft_str,
1853 | "striptags": do_striptags,
1854 | "sum": do_sum,
1855 | "title": do_title,
1856 | "trim": do_trim,
1857 | "truncate": do_truncate,
1858 | "unique": do_unique,
1859 | "upper": do_upper,
1860 | "urlencode": do_urlencode,
1861 | "urlize": do_urlize,
1862 | "wordcount": do_wordcount,
1863 | "wordwrap": do_wordwrap,
1864 | "xmlattr": do_xmlattr,
1865 | "tojson": do_tojson,
1866 | }
1867 |
```