This is page 53 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/bs4/builder/_lxml.py:
--------------------------------------------------------------------------------
```python
1 | # Use of this source code is governed by the MIT license.
2 | __license__ = "MIT"
3 |
4 | __all__ = [
5 | 'LXMLTreeBuilderForXML',
6 | 'LXMLTreeBuilder',
7 | ]
8 |
9 | try:
10 | from collections.abc import Callable # Python 3.6
11 | except ImportError as e:
12 | from collections import Callable
13 |
14 | from io import BytesIO
15 | from io import StringIO
16 | from lxml import etree
17 | from bs4.element import (
18 | Comment,
19 | Doctype,
20 | NamespacedAttribute,
21 | ProcessingInstruction,
22 | XMLProcessingInstruction,
23 | )
24 | from bs4.builder import (
25 | DetectsXMLParsedAsHTML,
26 | FAST,
27 | HTML,
28 | HTMLTreeBuilder,
29 | PERMISSIVE,
30 | ParserRejectedMarkup,
31 | TreeBuilder,
32 | XML)
33 | from bs4.dammit import EncodingDetector
34 |
35 | LXML = 'lxml'
36 |
37 | def _invert(d):
38 | "Invert a dictionary."
39 | return dict((v,k) for k, v in list(d.items()))
40 |
41 | class LXMLTreeBuilderForXML(TreeBuilder):
42 | DEFAULT_PARSER_CLASS = etree.XMLParser
43 |
44 | is_xml = True
45 | processing_instruction_class = XMLProcessingInstruction
46 |
47 | NAME = "lxml-xml"
48 | ALTERNATE_NAMES = ["xml"]
49 |
50 | # Well, it's permissive by XML parser standards.
51 | features = [NAME, LXML, XML, FAST, PERMISSIVE]
52 |
53 | CHUNK_SIZE = 512
54 |
55 | # This namespace mapping is specified in the XML Namespace
56 | # standard.
57 | DEFAULT_NSMAPS = dict(xml='http://www.w3.org/XML/1998/namespace')
58 |
59 | DEFAULT_NSMAPS_INVERTED = _invert(DEFAULT_NSMAPS)
60 |
61 | # NOTE: If we parsed Element objects and looked at .sourceline,
62 | # we'd be able to see the line numbers from the original document.
63 | # But instead we build an XMLParser or HTMLParser object to serve
64 | # as the target of parse messages, and those messages don't include
65 | # line numbers.
66 | # See: https://bugs.launchpad.net/lxml/+bug/1846906
67 |
68 | def initialize_soup(self, soup):
69 | """Let the BeautifulSoup object know about the standard namespace
70 | mapping.
71 |
72 | :param soup: A `BeautifulSoup`.
73 | """
74 | super(LXMLTreeBuilderForXML, self).initialize_soup(soup)
75 | self._register_namespaces(self.DEFAULT_NSMAPS)
76 |
77 | def _register_namespaces(self, mapping):
78 | """Let the BeautifulSoup object know about namespaces encountered
79 | while parsing the document.
80 |
81 | This might be useful later on when creating CSS selectors.
82 |
83 | This will track (almost) all namespaces, even ones that were
84 | only in scope for part of the document. If two namespaces have
85 | the same prefix, only the first one encountered will be
86 | tracked. Un-prefixed namespaces are not tracked.
87 |
88 | :param mapping: A dictionary mapping namespace prefixes to URIs.
89 | """
90 | for key, value in list(mapping.items()):
91 | # This is 'if key' and not 'if key is not None' because we
92 | # don't track un-prefixed namespaces. Soupselect will
93 | # treat an un-prefixed namespace as the default, which
94 | # causes confusion in some cases.
95 | if key and key not in self.soup._namespaces:
96 | # Let the BeautifulSoup object know about a new namespace.
97 | # If there are multiple namespaces defined with the same
98 | # prefix, the first one in the document takes precedence.
99 | self.soup._namespaces[key] = value
100 |
101 | def default_parser(self, encoding):
102 | """Find the default parser for the given encoding.
103 |
104 | :param encoding: A string.
105 | :return: Either a parser object or a class, which
106 | will be instantiated with default arguments.
107 | """
108 | if self._default_parser is not None:
109 | return self._default_parser
110 | return etree.XMLParser(
111 | target=self, strip_cdata=False, recover=True, encoding=encoding)
112 |
113 | def parser_for(self, encoding):
114 | """Instantiate an appropriate parser for the given encoding.
115 |
116 | :param encoding: A string.
117 | :return: A parser object such as an `etree.XMLParser`.
118 | """
119 | # Use the default parser.
120 | parser = self.default_parser(encoding)
121 |
122 | if isinstance(parser, Callable):
123 | # Instantiate the parser with default arguments
124 | parser = parser(
125 | target=self, strip_cdata=False, recover=True, encoding=encoding
126 | )
127 | return parser
128 |
129 | def __init__(self, parser=None, empty_element_tags=None, **kwargs):
130 | # TODO: Issue a warning if parser is present but not a
131 | # callable, since that means there's no way to create new
132 | # parsers for different encodings.
133 | self._default_parser = parser
134 | if empty_element_tags is not None:
135 | self.empty_element_tags = set(empty_element_tags)
136 | self.soup = None
137 | self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED]
138 | self.active_namespace_prefixes = [dict(self.DEFAULT_NSMAPS)]
139 | super(LXMLTreeBuilderForXML, self).__init__(**kwargs)
140 |
141 | def _getNsTag(self, tag):
142 | # Split the namespace URL out of a fully-qualified lxml tag
143 | # name. Copied from lxml's src/lxml/sax.py.
144 | if tag[0] == '{':
145 | return tuple(tag[1:].split('}', 1))
146 | else:
147 | return (None, tag)
148 |
149 | def prepare_markup(self, markup, user_specified_encoding=None,
150 | exclude_encodings=None,
151 | document_declared_encoding=None):
152 | """Run any preliminary steps necessary to make incoming markup
153 | acceptable to the parser.
154 |
155 | lxml really wants to get a bytestring and convert it to
156 | Unicode itself. So instead of using UnicodeDammit to convert
157 | the bytestring to Unicode using different encodings, this
158 | implementation uses EncodingDetector to iterate over the
159 | encodings, and tell lxml to try to parse the document as each
160 | one in turn.
161 |
162 | :param markup: Some markup -- hopefully a bytestring.
163 | :param user_specified_encoding: The user asked to try this encoding.
164 | :param document_declared_encoding: The markup itself claims to be
165 | in this encoding.
166 | :param exclude_encodings: The user asked _not_ to try any of
167 | these encodings.
168 |
169 | :yield: A series of 4-tuples:
170 | (markup, encoding, declared encoding,
171 | has undergone character replacement)
172 |
173 | Each 4-tuple represents a strategy for converting the
174 | document to Unicode and parsing it. Each strategy will be tried
175 | in turn.
176 | """
177 | is_html = not self.is_xml
178 | if is_html:
179 | self.processing_instruction_class = ProcessingInstruction
180 | # We're in HTML mode, so if we're given XML, that's worth
181 | # noting.
182 | DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml(
183 | markup, stacklevel=3
184 | )
185 | else:
186 | self.processing_instruction_class = XMLProcessingInstruction
187 |
188 | if isinstance(markup, str):
189 | # We were given Unicode. Maybe lxml can parse Unicode on
190 | # this system?
191 |
192 | # TODO: This is a workaround for
193 | # https://bugs.launchpad.net/lxml/+bug/1948551.
194 | # We can remove it once the upstream issue is fixed.
195 | if len(markup) > 0 and markup[0] == u'\N{BYTE ORDER MARK}':
196 | markup = markup[1:]
197 | yield markup, None, document_declared_encoding, False
198 |
199 | if isinstance(markup, str):
200 | # No, apparently not. Convert the Unicode to UTF-8 and
201 | # tell lxml to parse it as UTF-8.
202 | yield (markup.encode("utf8"), "utf8",
203 | document_declared_encoding, False)
204 |
205 | # This was provided by the end-user; treat it as a known
206 | # definite encoding per the algorithm laid out in the HTML5
207 | # spec. (See the EncodingDetector class for details.)
208 | known_definite_encodings = [user_specified_encoding]
209 |
210 | # This was found in the document; treat it as a slightly lower-priority
211 | # user encoding.
212 | user_encodings = [document_declared_encoding]
213 | detector = EncodingDetector(
214 | markup, known_definite_encodings=known_definite_encodings,
215 | user_encodings=user_encodings, is_html=is_html,
216 | exclude_encodings=exclude_encodings
217 | )
218 | for encoding in detector.encodings:
219 | yield (detector.markup, encoding, document_declared_encoding, False)
220 |
221 | def feed(self, markup):
222 | if isinstance(markup, bytes):
223 | markup = BytesIO(markup)
224 | elif isinstance(markup, str):
225 | markup = StringIO(markup)
226 |
227 | # Call feed() at least once, even if the markup is empty,
228 | # or the parser won't be initialized.
229 | data = markup.read(self.CHUNK_SIZE)
230 | try:
231 | self.parser = self.parser_for(self.soup.original_encoding)
232 | self.parser.feed(data)
233 | while len(data) != 0:
234 | # Now call feed() on the rest of the data, chunk by chunk.
235 | data = markup.read(self.CHUNK_SIZE)
236 | if len(data) != 0:
237 | self.parser.feed(data)
238 | self.parser.close()
239 | except (UnicodeDecodeError, LookupError, etree.ParserError) as e:
240 | raise ParserRejectedMarkup(e)
241 |
242 | def close(self):
243 | self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED]
244 |
245 | def start(self, name, attrs, nsmap={}):
246 | # Make sure attrs is a mutable dict--lxml may send an immutable dictproxy.
247 | attrs = dict(attrs)
248 | nsprefix = None
249 | # Invert each namespace map as it comes in.
250 | if len(nsmap) == 0 and len(self.nsmaps) > 1:
251 | # There are no new namespaces for this tag, but
252 | # non-default namespaces are in play, so we need a
253 | # separate tag stack to know when they end.
254 | self.nsmaps.append(None)
255 | elif len(nsmap) > 0:
256 | # A new namespace mapping has come into play.
257 |
258 | # First, Let the BeautifulSoup object know about it.
259 | self._register_namespaces(nsmap)
260 |
261 | # Then, add it to our running list of inverted namespace
262 | # mappings.
263 | self.nsmaps.append(_invert(nsmap))
264 |
265 | # The currently active namespace prefixes have
266 | # changed. Calculate the new mapping so it can be stored
267 | # with all Tag objects created while these prefixes are in
268 | # scope.
269 | current_mapping = dict(self.active_namespace_prefixes[-1])
270 | current_mapping.update(nsmap)
271 |
272 | # We should not track un-prefixed namespaces as we can only hold one
273 | # and it will be recognized as the default namespace by soupsieve,
274 | # which may be confusing in some situations.
275 | if '' in current_mapping:
276 | del current_mapping['']
277 | self.active_namespace_prefixes.append(current_mapping)
278 |
279 | # Also treat the namespace mapping as a set of attributes on the
280 | # tag, so we can recreate it later.
281 | attrs = attrs.copy()
282 | for prefix, namespace in list(nsmap.items()):
283 | attribute = NamespacedAttribute(
284 | "xmlns", prefix, "http://www.w3.org/2000/xmlns/")
285 | attrs[attribute] = namespace
286 |
287 | # Namespaces are in play. Find any attributes that came in
288 | # from lxml with namespaces attached to their names, and
289 | # turn then into NamespacedAttribute objects.
290 | new_attrs = {}
291 | for attr, value in list(attrs.items()):
292 | namespace, attr = self._getNsTag(attr)
293 | if namespace is None:
294 | new_attrs[attr] = value
295 | else:
296 | nsprefix = self._prefix_for_namespace(namespace)
297 | attr = NamespacedAttribute(nsprefix, attr, namespace)
298 | new_attrs[attr] = value
299 | attrs = new_attrs
300 |
301 | namespace, name = self._getNsTag(name)
302 | nsprefix = self._prefix_for_namespace(namespace)
303 | self.soup.handle_starttag(
304 | name, namespace, nsprefix, attrs,
305 | namespaces=self.active_namespace_prefixes[-1]
306 | )
307 |
308 | def _prefix_for_namespace(self, namespace):
309 | """Find the currently active prefix for the given namespace."""
310 | if namespace is None:
311 | return None
312 | for inverted_nsmap in reversed(self.nsmaps):
313 | if inverted_nsmap is not None and namespace in inverted_nsmap:
314 | return inverted_nsmap[namespace]
315 | return None
316 |
317 | def end(self, name):
318 | self.soup.endData()
319 | completed_tag = self.soup.tagStack[-1]
320 | namespace, name = self._getNsTag(name)
321 | nsprefix = None
322 | if namespace is not None:
323 | for inverted_nsmap in reversed(self.nsmaps):
324 | if inverted_nsmap is not None and namespace in inverted_nsmap:
325 | nsprefix = inverted_nsmap[namespace]
326 | break
327 | self.soup.handle_endtag(name, nsprefix)
328 | if len(self.nsmaps) > 1:
329 | # This tag, or one of its parents, introduced a namespace
330 | # mapping, so pop it off the stack.
331 | out_of_scope_nsmap = self.nsmaps.pop()
332 |
333 | if out_of_scope_nsmap is not None:
334 | # This tag introduced a namespace mapping which is no
335 | # longer in scope. Recalculate the currently active
336 | # namespace prefixes.
337 | self.active_namespace_prefixes.pop()
338 |
339 | def pi(self, target, data):
340 | self.soup.endData()
341 | data = target + ' ' + data
342 | self.soup.handle_data(data)
343 | self.soup.endData(self.processing_instruction_class)
344 |
345 | def data(self, content):
346 | self.soup.handle_data(content)
347 |
348 | def doctype(self, name, pubid, system):
349 | self.soup.endData()
350 | doctype = Doctype.for_name_and_ids(name, pubid, system)
351 | self.soup.object_was_parsed(doctype)
352 |
353 | def comment(self, content):
354 | "Handle comments as Comment objects."
355 | self.soup.endData()
356 | self.soup.handle_data(content)
357 | self.soup.endData(Comment)
358 |
359 | def test_fragment_to_document(self, fragment):
360 | """See `TreeBuilder`."""
361 | return '<?xml version="1.0" encoding="utf-8"?>\n%s' % fragment
362 |
363 |
364 | class LXMLTreeBuilder(HTMLTreeBuilder, LXMLTreeBuilderForXML):
365 |
366 | NAME = LXML
367 | ALTERNATE_NAMES = ["lxml-html"]
368 |
369 | features = ALTERNATE_NAMES + [NAME, HTML, FAST, PERMISSIVE]
370 | is_xml = False
371 | processing_instruction_class = ProcessingInstruction
372 |
373 | def default_parser(self, encoding):
374 | return etree.HTMLParser
375 |
376 | def feed(self, markup):
377 | encoding = self.soup.original_encoding
378 | try:
379 | self.parser = self.parser_for(encoding)
380 | self.parser.feed(markup)
381 | self.parser.close()
382 | except (UnicodeDecodeError, LookupError, etree.ParserError) as e:
383 | raise ParserRejectedMarkup(e)
384 |
385 |
386 | def test_fragment_to_document(self, fragment):
387 | """See `TreeBuilder`."""
388 | return '<html><body>%s</body></html>' % fragment
389 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/werkzeug/_reloader.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import fnmatch
4 | import os
5 | import subprocess
6 | import sys
7 | import threading
8 | import time
9 | import typing as t
10 | from itertools import chain
11 | from pathlib import PurePath
12 |
13 | from ._internal import _log
14 |
15 | # The various system prefixes where imports are found. Base values are
16 | # different when running in a virtualenv. All reloaders will ignore the
17 | # base paths (usually the system installation). The stat reloader won't
18 | # scan the virtualenv paths, it will only include modules that are
19 | # already imported.
20 | _ignore_always = tuple({sys.base_prefix, sys.base_exec_prefix})
21 | prefix = {*_ignore_always, sys.prefix, sys.exec_prefix}
22 |
23 | if hasattr(sys, "real_prefix"):
24 | # virtualenv < 20
25 | prefix.add(sys.real_prefix)
26 |
27 | _stat_ignore_scan = tuple(prefix)
28 | del prefix
29 | _ignore_common_dirs = {
30 | "__pycache__",
31 | ".git",
32 | ".hg",
33 | ".tox",
34 | ".nox",
35 | ".pytest_cache",
36 | ".mypy_cache",
37 | }
38 |
39 |
40 | def _iter_module_paths() -> t.Iterator[str]:
41 | """Find the filesystem paths associated with imported modules."""
42 | # List is in case the value is modified by the app while updating.
43 | for module in list(sys.modules.values()):
44 | name = getattr(module, "__file__", None)
45 |
46 | if name is None or name.startswith(_ignore_always):
47 | continue
48 |
49 | while not os.path.isfile(name):
50 | # Zip file, find the base file without the module path.
51 | old = name
52 | name = os.path.dirname(name)
53 |
54 | if name == old: # skip if it was all directories somehow
55 | break
56 | else:
57 | yield name
58 |
59 |
60 | def _remove_by_pattern(paths: set[str], exclude_patterns: set[str]) -> None:
61 | for pattern in exclude_patterns:
62 | paths.difference_update(fnmatch.filter(paths, pattern))
63 |
64 |
65 | def _find_stat_paths(
66 | extra_files: set[str], exclude_patterns: set[str]
67 | ) -> t.Iterable[str]:
68 | """Find paths for the stat reloader to watch. Returns imported
69 | module files, Python files under non-system paths. Extra files and
70 | Python files under extra directories can also be scanned.
71 |
72 | System paths have to be excluded for efficiency. Non-system paths,
73 | such as a project root or ``sys.path.insert``, should be the paths
74 | of interest to the user anyway.
75 | """
76 | paths = set()
77 |
78 | for path in chain(list(sys.path), extra_files):
79 | path = os.path.abspath(path)
80 |
81 | if os.path.isfile(path):
82 | # zip file on sys.path, or extra file
83 | paths.add(path)
84 | continue
85 |
86 | parent_has_py = {os.path.dirname(path): True}
87 |
88 | for root, dirs, files in os.walk(path):
89 | # Optimizations: ignore system prefixes, __pycache__ will
90 | # have a py or pyc module at the import path, ignore some
91 | # common known dirs such as version control and tool caches.
92 | if (
93 | root.startswith(_stat_ignore_scan)
94 | or os.path.basename(root) in _ignore_common_dirs
95 | ):
96 | dirs.clear()
97 | continue
98 |
99 | has_py = False
100 |
101 | for name in files:
102 | if name.endswith((".py", ".pyc")):
103 | has_py = True
104 | paths.add(os.path.join(root, name))
105 |
106 | # Optimization: stop scanning a directory if neither it nor
107 | # its parent contained Python files.
108 | if not (has_py or parent_has_py[os.path.dirname(root)]):
109 | dirs.clear()
110 | continue
111 |
112 | parent_has_py[root] = has_py
113 |
114 | paths.update(_iter_module_paths())
115 | _remove_by_pattern(paths, exclude_patterns)
116 | return paths
117 |
118 |
119 | def _find_watchdog_paths(
120 | extra_files: set[str], exclude_patterns: set[str]
121 | ) -> t.Iterable[str]:
122 | """Find paths for the stat reloader to watch. Looks at the same
123 | sources as the stat reloader, but watches everything under
124 | directories instead of individual files.
125 | """
126 | dirs = set()
127 |
128 | for name in chain(list(sys.path), extra_files):
129 | name = os.path.abspath(name)
130 |
131 | if os.path.isfile(name):
132 | name = os.path.dirname(name)
133 |
134 | dirs.add(name)
135 |
136 | for name in _iter_module_paths():
137 | dirs.add(os.path.dirname(name))
138 |
139 | _remove_by_pattern(dirs, exclude_patterns)
140 | return _find_common_roots(dirs)
141 |
142 |
143 | def _find_common_roots(paths: t.Iterable[str]) -> t.Iterable[str]:
144 | root: dict[str, dict[str, t.Any]] = {}
145 |
146 | for chunks in sorted((PurePath(x).parts for x in paths), key=len, reverse=True):
147 | node = root
148 |
149 | for chunk in chunks:
150 | node = node.setdefault(chunk, {})
151 |
152 | node.clear()
153 |
154 | rv = set()
155 |
156 | def _walk(node: t.Mapping[str, dict[str, t.Any]], path: tuple[str, ...]) -> None:
157 | for prefix, child in node.items():
158 | _walk(child, path + (prefix,))
159 |
160 | # If there are no more nodes, and a path has been accumulated, add it.
161 | # Path may be empty if the "" entry is in sys.path.
162 | if not node and path:
163 | rv.add(os.path.join(*path))
164 |
165 | _walk(root, ())
166 | return rv
167 |
168 |
169 | def _get_args_for_reloading() -> list[str]:
170 | """Determine how the script was executed, and return the args needed
171 | to execute it again in a new process.
172 | """
173 | if sys.version_info >= (3, 10):
174 | # sys.orig_argv, added in Python 3.10, contains the exact args used to invoke
175 | # Python. Still replace argv[0] with sys.executable for accuracy.
176 | return [sys.executable, *sys.orig_argv[1:]]
177 |
178 | rv = [sys.executable]
179 | py_script = sys.argv[0]
180 | args = sys.argv[1:]
181 | # Need to look at main module to determine how it was executed.
182 | __main__ = sys.modules["__main__"]
183 |
184 | # The value of __package__ indicates how Python was called. It may
185 | # not exist if a setuptools script is installed as an egg. It may be
186 | # set incorrectly for entry points created with pip on Windows.
187 | if getattr(__main__, "__package__", None) is None or (
188 | os.name == "nt"
189 | and __main__.__package__ == ""
190 | and not os.path.exists(py_script)
191 | and os.path.exists(f"{py_script}.exe")
192 | ):
193 | # Executed a file, like "python app.py".
194 | py_script = os.path.abspath(py_script)
195 |
196 | if os.name == "nt":
197 | # Windows entry points have ".exe" extension and should be
198 | # called directly.
199 | if not os.path.exists(py_script) and os.path.exists(f"{py_script}.exe"):
200 | py_script += ".exe"
201 |
202 | if (
203 | os.path.splitext(sys.executable)[1] == ".exe"
204 | and os.path.splitext(py_script)[1] == ".exe"
205 | ):
206 | rv.pop(0)
207 |
208 | rv.append(py_script)
209 | else:
210 | # Executed a module, like "python -m werkzeug.serving".
211 | if os.path.isfile(py_script):
212 | # Rewritten by Python from "-m script" to "/path/to/script.py".
213 | py_module = t.cast(str, __main__.__package__)
214 | name = os.path.splitext(os.path.basename(py_script))[0]
215 |
216 | if name != "__main__":
217 | py_module += f".{name}"
218 | else:
219 | # Incorrectly rewritten by pydevd debugger from "-m script" to "script".
220 | py_module = py_script
221 |
222 | rv.extend(("-m", py_module.lstrip(".")))
223 |
224 | rv.extend(args)
225 | return rv
226 |
227 |
228 | class ReloaderLoop:
229 | name = ""
230 |
231 | def __init__(
232 | self,
233 | extra_files: t.Iterable[str] | None = None,
234 | exclude_patterns: t.Iterable[str] | None = None,
235 | interval: int | float = 1,
236 | ) -> None:
237 | self.extra_files: set[str] = {os.path.abspath(x) for x in extra_files or ()}
238 | self.exclude_patterns: set[str] = set(exclude_patterns or ())
239 | self.interval = interval
240 |
241 | def __enter__(self) -> ReloaderLoop:
242 | """Do any setup, then run one step of the watch to populate the
243 | initial filesystem state.
244 | """
245 | self.run_step()
246 | return self
247 |
248 | def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore
249 | """Clean up any resources associated with the reloader."""
250 | pass
251 |
252 | def run(self) -> None:
253 | """Continually run the watch step, sleeping for the configured
254 | interval after each step.
255 | """
256 | while True:
257 | self.run_step()
258 | time.sleep(self.interval)
259 |
260 | def run_step(self) -> None:
261 | """Run one step for watching the filesystem. Called once to set
262 | up initial state, then repeatedly to update it.
263 | """
264 | pass
265 |
266 | def restart_with_reloader(self) -> int:
267 | """Spawn a new Python interpreter with the same arguments as the
268 | current one, but running the reloader thread.
269 | """
270 | while True:
271 | _log("info", f" * Restarting with {self.name}")
272 | args = _get_args_for_reloading()
273 | new_environ = os.environ.copy()
274 | new_environ["WERKZEUG_RUN_MAIN"] = "true"
275 | exit_code = subprocess.call(args, env=new_environ, close_fds=False)
276 |
277 | if exit_code != 3:
278 | return exit_code
279 |
280 | def trigger_reload(self, filename: str) -> None:
281 | self.log_reload(filename)
282 | sys.exit(3)
283 |
284 | def log_reload(self, filename: str) -> None:
285 | filename = os.path.abspath(filename)
286 | _log("info", f" * Detected change in {filename!r}, reloading")
287 |
288 |
289 | class StatReloaderLoop(ReloaderLoop):
290 | name = "stat"
291 |
292 | def __enter__(self) -> ReloaderLoop:
293 | self.mtimes: dict[str, float] = {}
294 | return super().__enter__()
295 |
296 | def run_step(self) -> None:
297 | for name in _find_stat_paths(self.extra_files, self.exclude_patterns):
298 | try:
299 | mtime = os.stat(name).st_mtime
300 | except OSError:
301 | continue
302 |
303 | old_time = self.mtimes.get(name)
304 |
305 | if old_time is None:
306 | self.mtimes[name] = mtime
307 | continue
308 |
309 | if mtime > old_time:
310 | self.trigger_reload(name)
311 |
312 |
313 | class WatchdogReloaderLoop(ReloaderLoop):
314 | def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
315 | from watchdog.events import EVENT_TYPE_OPENED
316 | from watchdog.events import FileModifiedEvent
317 | from watchdog.events import PatternMatchingEventHandler
318 | from watchdog.observers import Observer
319 |
320 | super().__init__(*args, **kwargs)
321 | trigger_reload = self.trigger_reload
322 |
323 | class EventHandler(PatternMatchingEventHandler):
324 | def on_any_event(self, event: FileModifiedEvent): # type: ignore
325 | if event.event_type == EVENT_TYPE_OPENED:
326 | return
327 |
328 | trigger_reload(event.src_path)
329 |
330 | reloader_name = Observer.__name__.lower() # type: ignore[attr-defined]
331 |
332 | if reloader_name.endswith("observer"):
333 | reloader_name = reloader_name[:-8]
334 |
335 | self.name = f"watchdog ({reloader_name})"
336 | self.observer = Observer()
337 | # Extra patterns can be non-Python files, match them in addition
338 | # to all Python files in default and extra directories. Ignore
339 | # __pycache__ since a change there will always have a change to
340 | # the source file (or initial pyc file) as well. Ignore Git and
341 | # Mercurial internal changes.
342 | extra_patterns = [p for p in self.extra_files if not os.path.isdir(p)]
343 | self.event_handler = EventHandler( # type: ignore[no-untyped-call]
344 | patterns=["*.py", "*.pyc", "*.zip", *extra_patterns],
345 | ignore_patterns=[
346 | *[f"*/{d}/*" for d in _ignore_common_dirs],
347 | *self.exclude_patterns,
348 | ],
349 | )
350 | self.should_reload = False
351 |
352 | def trigger_reload(self, filename: str) -> None:
353 | # This is called inside an event handler, which means throwing
354 | # SystemExit has no effect.
355 | # https://github.com/gorakhargosh/watchdog/issues/294
356 | self.should_reload = True
357 | self.log_reload(filename)
358 |
359 | def __enter__(self) -> ReloaderLoop:
360 | self.watches: dict[str, t.Any] = {}
361 | self.observer.start() # type: ignore[no-untyped-call]
362 | return super().__enter__()
363 |
364 | def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore
365 | self.observer.stop() # type: ignore[no-untyped-call]
366 | self.observer.join()
367 |
368 | def run(self) -> None:
369 | while not self.should_reload:
370 | self.run_step()
371 | time.sleep(self.interval)
372 |
373 | sys.exit(3)
374 |
375 | def run_step(self) -> None:
376 | to_delete = set(self.watches)
377 |
378 | for path in _find_watchdog_paths(self.extra_files, self.exclude_patterns):
379 | if path not in self.watches:
380 | try:
381 | self.watches[path] = self.observer.schedule( # type: ignore[no-untyped-call]
382 | self.event_handler, path, recursive=True
383 | )
384 | except OSError:
385 | # Clear this path from list of watches We don't want
386 | # the same error message showing again in the next
387 | # iteration.
388 | self.watches[path] = None
389 |
390 | to_delete.discard(path)
391 |
392 | for path in to_delete:
393 | watch = self.watches.pop(path, None)
394 |
395 | if watch is not None:
396 | self.observer.unschedule(watch) # type: ignore[no-untyped-call]
397 |
398 |
399 | reloader_loops: dict[str, type[ReloaderLoop]] = {
400 | "stat": StatReloaderLoop,
401 | "watchdog": WatchdogReloaderLoop,
402 | }
403 |
404 | try:
405 | __import__("watchdog.observers")
406 | except ImportError:
407 | reloader_loops["auto"] = reloader_loops["stat"]
408 | else:
409 | reloader_loops["auto"] = reloader_loops["watchdog"]
410 |
411 |
412 | def ensure_echo_on() -> None:
413 | """Ensure that echo mode is enabled. Some tools such as PDB disable
414 | it which causes usability issues after a reload."""
415 | # tcgetattr will fail if stdin isn't a tty
416 | if sys.stdin is None or not sys.stdin.isatty():
417 | return
418 |
419 | try:
420 | import termios
421 | except ImportError:
422 | return
423 |
424 | attributes = termios.tcgetattr(sys.stdin)
425 |
426 | if not attributes[3] & termios.ECHO:
427 | attributes[3] |= termios.ECHO
428 | termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes)
429 |
430 |
431 | def run_with_reloader(
432 | main_func: t.Callable[[], None],
433 | extra_files: t.Iterable[str] | None = None,
434 | exclude_patterns: t.Iterable[str] | None = None,
435 | interval: int | float = 1,
436 | reloader_type: str = "auto",
437 | ) -> None:
438 | """Run the given function in an independent Python interpreter."""
439 | import signal
440 |
441 | signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
442 | reloader = reloader_loops[reloader_type](
443 | extra_files=extra_files, exclude_patterns=exclude_patterns, interval=interval
444 | )
445 |
446 | try:
447 | if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
448 | ensure_echo_on()
449 | t = threading.Thread(target=main_func, args=())
450 | t.daemon = True
451 |
452 | # Enter the reloader to set up initial state, then start
453 | # the app thread and reloader update loop.
454 | with reloader:
455 | t.start()
456 | reloader.run()
457 | else:
458 | sys.exit(reloader.restart_with_reloader())
459 | except KeyboardInterrupt:
460 | pass
461 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/flask/ctx.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import contextvars
4 | import sys
5 | import typing as t
6 | from functools import update_wrapper
7 | from types import TracebackType
8 |
9 | from werkzeug.exceptions import HTTPException
10 |
11 | from . import typing as ft
12 | from .globals import _cv_app
13 | from .globals import _cv_request
14 | from .signals import appcontext_popped
15 | from .signals import appcontext_pushed
16 |
17 | if t.TYPE_CHECKING: # pragma: no cover
18 | from _typeshed.wsgi import WSGIEnvironment
19 |
20 | from .app import Flask
21 | from .sessions import SessionMixin
22 | from .wrappers import Request
23 |
24 |
25 | # a singleton sentinel value for parameter defaults
26 | _sentinel = object()
27 |
28 |
29 | class _AppCtxGlobals:
30 | """A plain object. Used as a namespace for storing data during an
31 | application context.
32 |
33 | Creating an app context automatically creates this object, which is
34 | made available as the :data:`g` proxy.
35 |
36 | .. describe:: 'key' in g
37 |
38 | Check whether an attribute is present.
39 |
40 | .. versionadded:: 0.10
41 |
42 | .. describe:: iter(g)
43 |
44 | Return an iterator over the attribute names.
45 |
46 | .. versionadded:: 0.10
47 | """
48 |
49 | # Define attr methods to let mypy know this is a namespace object
50 | # that has arbitrary attributes.
51 |
52 | def __getattr__(self, name: str) -> t.Any:
53 | try:
54 | return self.__dict__[name]
55 | except KeyError:
56 | raise AttributeError(name) from None
57 |
58 | def __setattr__(self, name: str, value: t.Any) -> None:
59 | self.__dict__[name] = value
60 |
61 | def __delattr__(self, name: str) -> None:
62 | try:
63 | del self.__dict__[name]
64 | except KeyError:
65 | raise AttributeError(name) from None
66 |
67 | def get(self, name: str, default: t.Any | None = None) -> t.Any:
68 | """Get an attribute by name, or a default value. Like
69 | :meth:`dict.get`.
70 |
71 | :param name: Name of attribute to get.
72 | :param default: Value to return if the attribute is not present.
73 |
74 | .. versionadded:: 0.10
75 | """
76 | return self.__dict__.get(name, default)
77 |
78 | def pop(self, name: str, default: t.Any = _sentinel) -> t.Any:
79 | """Get and remove an attribute by name. Like :meth:`dict.pop`.
80 |
81 | :param name: Name of attribute to pop.
82 | :param default: Value to return if the attribute is not present,
83 | instead of raising a ``KeyError``.
84 |
85 | .. versionadded:: 0.11
86 | """
87 | if default is _sentinel:
88 | return self.__dict__.pop(name)
89 | else:
90 | return self.__dict__.pop(name, default)
91 |
92 | def setdefault(self, name: str, default: t.Any = None) -> t.Any:
93 | """Get the value of an attribute if it is present, otherwise
94 | set and return a default value. Like :meth:`dict.setdefault`.
95 |
96 | :param name: Name of attribute to get.
97 | :param default: Value to set and return if the attribute is not
98 | present.
99 |
100 | .. versionadded:: 0.11
101 | """
102 | return self.__dict__.setdefault(name, default)
103 |
104 | def __contains__(self, item: str) -> bool:
105 | return item in self.__dict__
106 |
107 | def __iter__(self) -> t.Iterator[str]:
108 | return iter(self.__dict__)
109 |
110 | def __repr__(self) -> str:
111 | ctx = _cv_app.get(None)
112 | if ctx is not None:
113 | return f"<flask.g of '{ctx.app.name}'>"
114 | return object.__repr__(self)
115 |
116 |
117 | def after_this_request(
118 | f: ft.AfterRequestCallable[t.Any],
119 | ) -> ft.AfterRequestCallable[t.Any]:
120 | """Executes a function after this request. This is useful to modify
121 | response objects. The function is passed the response object and has
122 | to return the same or a new one.
123 |
124 | Example::
125 |
126 | @app.route('/')
127 | def index():
128 | @after_this_request
129 | def add_header(response):
130 | response.headers['X-Foo'] = 'Parachute'
131 | return response
132 | return 'Hello World!'
133 |
134 | This is more useful if a function other than the view function wants to
135 | modify a response. For instance think of a decorator that wants to add
136 | some headers without converting the return value into a response object.
137 |
138 | .. versionadded:: 0.9
139 | """
140 | ctx = _cv_request.get(None)
141 |
142 | if ctx is None:
143 | raise RuntimeError(
144 | "'after_this_request' can only be used when a request"
145 | " context is active, such as in a view function."
146 | )
147 |
148 | ctx._after_request_functions.append(f)
149 | return f
150 |
151 |
152 | F = t.TypeVar("F", bound=t.Callable[..., t.Any])
153 |
154 |
155 | def copy_current_request_context(f: F) -> F:
156 | """A helper function that decorates a function to retain the current
157 | request context. This is useful when working with greenlets. The moment
158 | the function is decorated a copy of the request context is created and
159 | then pushed when the function is called. The current session is also
160 | included in the copied request context.
161 |
162 | Example::
163 |
164 | import gevent
165 | from flask import copy_current_request_context
166 |
167 | @app.route('/')
168 | def index():
169 | @copy_current_request_context
170 | def do_some_work():
171 | # do some work here, it can access flask.request or
172 | # flask.session like you would otherwise in the view function.
173 | ...
174 | gevent.spawn(do_some_work)
175 | return 'Regular response'
176 |
177 | .. versionadded:: 0.10
178 | """
179 | ctx = _cv_request.get(None)
180 |
181 | if ctx is None:
182 | raise RuntimeError(
183 | "'copy_current_request_context' can only be used when a"
184 | " request context is active, such as in a view function."
185 | )
186 |
187 | ctx = ctx.copy()
188 |
189 | def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
190 | with ctx: # type: ignore[union-attr]
191 | return ctx.app.ensure_sync(f)(*args, **kwargs) # type: ignore[union-attr]
192 |
193 | return update_wrapper(wrapper, f) # type: ignore[return-value]
194 |
195 |
196 | def has_request_context() -> bool:
197 | """If you have code that wants to test if a request context is there or
198 | not this function can be used. For instance, you may want to take advantage
199 | of request information if the request object is available, but fail
200 | silently if it is unavailable.
201 |
202 | ::
203 |
204 | class User(db.Model):
205 |
206 | def __init__(self, username, remote_addr=None):
207 | self.username = username
208 | if remote_addr is None and has_request_context():
209 | remote_addr = request.remote_addr
210 | self.remote_addr = remote_addr
211 |
212 | Alternatively you can also just test any of the context bound objects
213 | (such as :class:`request` or :class:`g`) for truthness::
214 |
215 | class User(db.Model):
216 |
217 | def __init__(self, username, remote_addr=None):
218 | self.username = username
219 | if remote_addr is None and request:
220 | remote_addr = request.remote_addr
221 | self.remote_addr = remote_addr
222 |
223 | .. versionadded:: 0.7
224 | """
225 | return _cv_request.get(None) is not None
226 |
227 |
228 | def has_app_context() -> bool:
229 | """Works like :func:`has_request_context` but for the application
230 | context. You can also just do a boolean check on the
231 | :data:`current_app` object instead.
232 |
233 | .. versionadded:: 0.9
234 | """
235 | return _cv_app.get(None) is not None
236 |
237 |
238 | class AppContext:
239 | """The app context contains application-specific information. An app
240 | context is created and pushed at the beginning of each request if
241 | one is not already active. An app context is also pushed when
242 | running CLI commands.
243 | """
244 |
245 | def __init__(self, app: Flask) -> None:
246 | self.app = app
247 | self.url_adapter = app.create_url_adapter(None)
248 | self.g: _AppCtxGlobals = app.app_ctx_globals_class()
249 | self._cv_tokens: list[contextvars.Token[AppContext]] = []
250 |
251 | def push(self) -> None:
252 | """Binds the app context to the current context."""
253 | self._cv_tokens.append(_cv_app.set(self))
254 | appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)
255 |
256 | def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore
257 | """Pops the app context."""
258 | try:
259 | if len(self._cv_tokens) == 1:
260 | if exc is _sentinel:
261 | exc = sys.exc_info()[1]
262 | self.app.do_teardown_appcontext(exc)
263 | finally:
264 | ctx = _cv_app.get()
265 | _cv_app.reset(self._cv_tokens.pop())
266 |
267 | if ctx is not self:
268 | raise AssertionError(
269 | f"Popped wrong app context. ({ctx!r} instead of {self!r})"
270 | )
271 |
272 | appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync)
273 |
274 | def __enter__(self) -> AppContext:
275 | self.push()
276 | return self
277 |
278 | def __exit__(
279 | self,
280 | exc_type: type | None,
281 | exc_value: BaseException | None,
282 | tb: TracebackType | None,
283 | ) -> None:
284 | self.pop(exc_value)
285 |
286 |
287 | class RequestContext:
288 | """The request context contains per-request information. The Flask
289 | app creates and pushes it at the beginning of the request, then pops
290 | it at the end of the request. It will create the URL adapter and
291 | request object for the WSGI environment provided.
292 |
293 | Do not attempt to use this class directly, instead use
294 | :meth:`~flask.Flask.test_request_context` and
295 | :meth:`~flask.Flask.request_context` to create this object.
296 |
297 | When the request context is popped, it will evaluate all the
298 | functions registered on the application for teardown execution
299 | (:meth:`~flask.Flask.teardown_request`).
300 |
301 | The request context is automatically popped at the end of the
302 | request. When using the interactive debugger, the context will be
303 | restored so ``request`` is still accessible. Similarly, the test
304 | client can preserve the context after the request ends. However,
305 | teardown functions may already have closed some resources such as
306 | database connections.
307 | """
308 |
309 | def __init__(
310 | self,
311 | app: Flask,
312 | environ: WSGIEnvironment,
313 | request: Request | None = None,
314 | session: SessionMixin | None = None,
315 | ) -> None:
316 | self.app = app
317 | if request is None:
318 | request = app.request_class(environ)
319 | request.json_module = app.json
320 | self.request: Request = request
321 | self.url_adapter = None
322 | try:
323 | self.url_adapter = app.create_url_adapter(self.request)
324 | except HTTPException as e:
325 | self.request.routing_exception = e
326 | self.flashes: list[tuple[str, str]] | None = None
327 | self.session: SessionMixin | None = session
328 | # Functions that should be executed after the request on the response
329 | # object. These will be called before the regular "after_request"
330 | # functions.
331 | self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = []
332 |
333 | self._cv_tokens: list[
334 | tuple[contextvars.Token[RequestContext], AppContext | None]
335 | ] = []
336 |
337 | def copy(self) -> RequestContext:
338 | """Creates a copy of this request context with the same request object.
339 | This can be used to move a request context to a different greenlet.
340 | Because the actual request object is the same this cannot be used to
341 | move a request context to a different thread unless access to the
342 | request object is locked.
343 |
344 | .. versionadded:: 0.10
345 |
346 | .. versionchanged:: 1.1
347 | The current session object is used instead of reloading the original
348 | data. This prevents `flask.session` pointing to an out-of-date object.
349 | """
350 | return self.__class__(
351 | self.app,
352 | environ=self.request.environ,
353 | request=self.request,
354 | session=self.session,
355 | )
356 |
357 | def match_request(self) -> None:
358 | """Can be overridden by a subclass to hook into the matching
359 | of the request.
360 | """
361 | try:
362 | result = self.url_adapter.match(return_rule=True) # type: ignore
363 | self.request.url_rule, self.request.view_args = result # type: ignore
364 | except HTTPException as e:
365 | self.request.routing_exception = e
366 |
367 | def push(self) -> None:
368 | # Before we push the request context we have to ensure that there
369 | # is an application context.
370 | app_ctx = _cv_app.get(None)
371 |
372 | if app_ctx is None or app_ctx.app is not self.app:
373 | app_ctx = self.app.app_context()
374 | app_ctx.push()
375 | else:
376 | app_ctx = None
377 |
378 | self._cv_tokens.append((_cv_request.set(self), app_ctx))
379 |
380 | # Open the session at the moment that the request context is available.
381 | # This allows a custom open_session method to use the request context.
382 | # Only open a new session if this is the first time the request was
383 | # pushed, otherwise stream_with_context loses the session.
384 | if self.session is None:
385 | session_interface = self.app.session_interface
386 | self.session = session_interface.open_session(self.app, self.request)
387 |
388 | if self.session is None:
389 | self.session = session_interface.make_null_session(self.app)
390 |
391 | # Match the request URL after loading the session, so that the
392 | # session is available in custom URL converters.
393 | if self.url_adapter is not None:
394 | self.match_request()
395 |
396 | def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore
397 | """Pops the request context and unbinds it by doing that. This will
398 | also trigger the execution of functions registered by the
399 | :meth:`~flask.Flask.teardown_request` decorator.
400 |
401 | .. versionchanged:: 0.9
402 | Added the `exc` argument.
403 | """
404 | clear_request = len(self._cv_tokens) == 1
405 |
406 | try:
407 | if clear_request:
408 | if exc is _sentinel:
409 | exc = sys.exc_info()[1]
410 | self.app.do_teardown_request(exc)
411 |
412 | request_close = getattr(self.request, "close", None)
413 | if request_close is not None:
414 | request_close()
415 | finally:
416 | ctx = _cv_request.get()
417 | token, app_ctx = self._cv_tokens.pop()
418 | _cv_request.reset(token)
419 |
420 | # get rid of circular dependencies at the end of the request
421 | # so that we don't require the GC to be active.
422 | if clear_request:
423 | ctx.request.environ["werkzeug.request"] = None
424 |
425 | if app_ctx is not None:
426 | app_ctx.pop(exc)
427 |
428 | if ctx is not self:
429 | raise AssertionError(
430 | f"Popped wrong request context. ({ctx!r} instead of {self!r})"
431 | )
432 |
433 | def __enter__(self) -> RequestContext:
434 | self.push()
435 | return self
436 |
437 | def __exit__(
438 | self,
439 | exc_type: type | None,
440 | exc_value: BaseException | None,
441 | tb: TracebackType | None,
442 | ) -> None:
443 | self.pop(exc_value)
444 |
445 | def __repr__(self) -> str:
446 | return (
447 | f"<{type(self).__name__} {self.request.url!r}"
448 | f" [{self.request.method}] of {self.app.name}>"
449 | )
450 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/urllib3/util/url.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 |
3 | import re
4 | import typing
5 |
6 | from ..exceptions import LocationParseError
7 | from .util import to_str
8 |
9 | # We only want to normalize urls with an HTTP(S) scheme.
10 | # urllib3 infers URLs without a scheme (None) to be http.
11 | _NORMALIZABLE_SCHEMES = ("http", "https", None)
12 |
13 | # Almost all of these patterns were derived from the
14 | # 'rfc3986' module: https://github.com/python-hyper/rfc3986
15 | _PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}")
16 | _SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)")
17 | _URI_RE = re.compile(
18 | r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?"
19 | r"(?://([^\\/?#]*))?"
20 | r"([^?#]*)"
21 | r"(?:\?([^#]*))?"
22 | r"(?:#(.*))?$",
23 | re.UNICODE | re.DOTALL,
24 | )
25 |
26 | _IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}"
27 | _HEX_PAT = "[0-9A-Fa-f]{1,4}"
28 | _LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=_HEX_PAT, ipv4=_IPV4_PAT)
29 | _subs = {"hex": _HEX_PAT, "ls32": _LS32_PAT}
30 | _variations = [
31 | # 6( h16 ":" ) ls32
32 | "(?:%(hex)s:){6}%(ls32)s",
33 | # "::" 5( h16 ":" ) ls32
34 | "::(?:%(hex)s:){5}%(ls32)s",
35 | # [ h16 ] "::" 4( h16 ":" ) ls32
36 | "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s",
37 | # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
38 | "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s",
39 | # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
40 | "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s",
41 | # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
42 | "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s",
43 | # [ *4( h16 ":" ) h16 ] "::" ls32
44 | "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s",
45 | # [ *5( h16 ":" ) h16 ] "::" h16
46 | "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s",
47 | # [ *6( h16 ":" ) h16 ] "::"
48 | "(?:(?:%(hex)s:){0,6}%(hex)s)?::",
49 | ]
50 |
51 | _UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~"
52 | _IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")"
53 | _ZONE_ID_PAT = "(?:%25|%)(?:[" + _UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+"
54 | _IPV6_ADDRZ_PAT = r"\[" + _IPV6_PAT + r"(?:" + _ZONE_ID_PAT + r")?\]"
55 | _REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*"
56 | _TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$")
57 |
58 | _IPV4_RE = re.compile("^" + _IPV4_PAT + "$")
59 | _IPV6_RE = re.compile("^" + _IPV6_PAT + "$")
60 | _IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT + "$")
61 | _BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT[2:-2] + "$")
62 | _ZONE_ID_RE = re.compile("(" + _ZONE_ID_PAT + r")\]$")
63 |
64 | _HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % (
65 | _REG_NAME_PAT,
66 | _IPV4_PAT,
67 | _IPV6_ADDRZ_PAT,
68 | )
69 | _HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL)
70 |
71 | _UNRESERVED_CHARS = set(
72 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~"
73 | )
74 | _SUB_DELIM_CHARS = set("!$&'()*+,;=")
75 | _USERINFO_CHARS = _UNRESERVED_CHARS | _SUB_DELIM_CHARS | {":"}
76 | _PATH_CHARS = _USERINFO_CHARS | {"@", "/"}
77 | _QUERY_CHARS = _FRAGMENT_CHARS = _PATH_CHARS | {"?"}
78 |
79 |
80 | class Url(
81 | typing.NamedTuple(
82 | "Url",
83 | [
84 | ("scheme", typing.Optional[str]),
85 | ("auth", typing.Optional[str]),
86 | ("host", typing.Optional[str]),
87 | ("port", typing.Optional[int]),
88 | ("path", typing.Optional[str]),
89 | ("query", typing.Optional[str]),
90 | ("fragment", typing.Optional[str]),
91 | ],
92 | )
93 | ):
94 | """
95 | Data structure for representing an HTTP URL. Used as a return value for
96 | :func:`parse_url`. Both the scheme and host are normalized as they are
97 | both case-insensitive according to RFC 3986.
98 | """
99 |
100 | def __new__( # type: ignore[no-untyped-def]
101 | cls,
102 | scheme: str | None = None,
103 | auth: str | None = None,
104 | host: str | None = None,
105 | port: int | None = None,
106 | path: str | None = None,
107 | query: str | None = None,
108 | fragment: str | None = None,
109 | ):
110 | if path and not path.startswith("/"):
111 | path = "/" + path
112 | if scheme is not None:
113 | scheme = scheme.lower()
114 | return super().__new__(cls, scheme, auth, host, port, path, query, fragment)
115 |
116 | @property
117 | def hostname(self) -> str | None:
118 | """For backwards-compatibility with urlparse. We're nice like that."""
119 | return self.host
120 |
121 | @property
122 | def request_uri(self) -> str:
123 | """Absolute path including the query string."""
124 | uri = self.path or "/"
125 |
126 | if self.query is not None:
127 | uri += "?" + self.query
128 |
129 | return uri
130 |
131 | @property
132 | def authority(self) -> str | None:
133 | """
134 | Authority component as defined in RFC 3986 3.2.
135 | This includes userinfo (auth), host and port.
136 |
137 | i.e.
138 | userinfo@host:port
139 | """
140 | userinfo = self.auth
141 | netloc = self.netloc
142 | if netloc is None or userinfo is None:
143 | return netloc
144 | else:
145 | return f"{userinfo}@{netloc}"
146 |
147 | @property
148 | def netloc(self) -> str | None:
149 | """
150 | Network location including host and port.
151 |
152 | If you need the equivalent of urllib.parse's ``netloc``,
153 | use the ``authority`` property instead.
154 | """
155 | if self.host is None:
156 | return None
157 | if self.port:
158 | return f"{self.host}:{self.port}"
159 | return self.host
160 |
161 | @property
162 | def url(self) -> str:
163 | """
164 | Convert self into a url
165 |
166 | This function should more or less round-trip with :func:`.parse_url`. The
167 | returned url may not be exactly the same as the url inputted to
168 | :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls
169 | with a blank port will have : removed).
170 |
171 | Example:
172 |
173 | .. code-block:: python
174 |
175 | import urllib3
176 |
177 | U = urllib3.util.parse_url("https://google.com/mail/")
178 |
179 | print(U.url)
180 | # "https://google.com/mail/"
181 |
182 | print( urllib3.util.Url("https", "username:password",
183 | "host.com", 80, "/path", "query", "fragment"
184 | ).url
185 | )
186 | # "https://username:[email protected]:80/path?query#fragment"
187 | """
188 | scheme, auth, host, port, path, query, fragment = self
189 | url = ""
190 |
191 | # We use "is not None" we want things to happen with empty strings (or 0 port)
192 | if scheme is not None:
193 | url += scheme + "://"
194 | if auth is not None:
195 | url += auth + "@"
196 | if host is not None:
197 | url += host
198 | if port is not None:
199 | url += ":" + str(port)
200 | if path is not None:
201 | url += path
202 | if query is not None:
203 | url += "?" + query
204 | if fragment is not None:
205 | url += "#" + fragment
206 |
207 | return url
208 |
209 | def __str__(self) -> str:
210 | return self.url
211 |
212 |
213 | @typing.overload
214 | def _encode_invalid_chars(
215 | component: str, allowed_chars: typing.Container[str]
216 | ) -> str: # Abstract
217 | ...
218 |
219 |
220 | @typing.overload
221 | def _encode_invalid_chars(
222 | component: None, allowed_chars: typing.Container[str]
223 | ) -> None: # Abstract
224 | ...
225 |
226 |
227 | def _encode_invalid_chars(
228 | component: str | None, allowed_chars: typing.Container[str]
229 | ) -> str | None:
230 | """Percent-encodes a URI component without reapplying
231 | onto an already percent-encoded component.
232 | """
233 | if component is None:
234 | return component
235 |
236 | component = to_str(component)
237 |
238 | # Normalize existing percent-encoded bytes.
239 | # Try to see if the component we're encoding is already percent-encoded
240 | # so we can skip all '%' characters but still encode all others.
241 | component, percent_encodings = _PERCENT_RE.subn(
242 | lambda match: match.group(0).upper(), component
243 | )
244 |
245 | uri_bytes = component.encode("utf-8", "surrogatepass")
246 | is_percent_encoded = percent_encodings == uri_bytes.count(b"%")
247 | encoded_component = bytearray()
248 |
249 | for i in range(0, len(uri_bytes)):
250 | # Will return a single character bytestring
251 | byte = uri_bytes[i : i + 1]
252 | byte_ord = ord(byte)
253 | if (is_percent_encoded and byte == b"%") or (
254 | byte_ord < 128 and byte.decode() in allowed_chars
255 | ):
256 | encoded_component += byte
257 | continue
258 | encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper()))
259 |
260 | return encoded_component.decode()
261 |
262 |
263 | def _remove_path_dot_segments(path: str) -> str:
264 | # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code
265 | segments = path.split("/") # Turn the path into a list of segments
266 | output = [] # Initialize the variable to use to store output
267 |
268 | for segment in segments:
269 | # '.' is the current directory, so ignore it, it is superfluous
270 | if segment == ".":
271 | continue
272 | # Anything other than '..', should be appended to the output
273 | if segment != "..":
274 | output.append(segment)
275 | # In this case segment == '..', if we can, we should pop the last
276 | # element
277 | elif output:
278 | output.pop()
279 |
280 | # If the path starts with '/' and the output is empty or the first string
281 | # is non-empty
282 | if path.startswith("/") and (not output or output[0]):
283 | output.insert(0, "")
284 |
285 | # If the path starts with '/.' or '/..' ensure we add one more empty
286 | # string to add a trailing '/'
287 | if path.endswith(("/.", "/..")):
288 | output.append("")
289 |
290 | return "/".join(output)
291 |
292 |
293 | @typing.overload
294 | def _normalize_host(host: None, scheme: str | None) -> None:
295 | ...
296 |
297 |
298 | @typing.overload
299 | def _normalize_host(host: str, scheme: str | None) -> str:
300 | ...
301 |
302 |
303 | def _normalize_host(host: str | None, scheme: str | None) -> str | None:
304 | if host:
305 | if scheme in _NORMALIZABLE_SCHEMES:
306 | is_ipv6 = _IPV6_ADDRZ_RE.match(host)
307 | if is_ipv6:
308 | # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as
309 | # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID
310 | # separator as necessary to return a valid RFC 4007 scoped IP.
311 | match = _ZONE_ID_RE.search(host)
312 | if match:
313 | start, end = match.span(1)
314 | zone_id = host[start:end]
315 |
316 | if zone_id.startswith("%25") and zone_id != "%25":
317 | zone_id = zone_id[3:]
318 | else:
319 | zone_id = zone_id[1:]
320 | zone_id = _encode_invalid_chars(zone_id, _UNRESERVED_CHARS)
321 | return f"{host[:start].lower()}%{zone_id}{host[end:]}"
322 | else:
323 | return host.lower()
324 | elif not _IPV4_RE.match(host):
325 | return to_str(
326 | b".".join([_idna_encode(label) for label in host.split(".")]),
327 | "ascii",
328 | )
329 | return host
330 |
331 |
332 | def _idna_encode(name: str) -> bytes:
333 | if not name.isascii():
334 | try:
335 | import idna
336 | except ImportError:
337 | raise LocationParseError(
338 | "Unable to parse URL without the 'idna' module"
339 | ) from None
340 |
341 | try:
342 | return idna.encode(name.lower(), strict=True, std3_rules=True)
343 | except idna.IDNAError:
344 | raise LocationParseError(
345 | f"Name '{name}' is not a valid IDNA label"
346 | ) from None
347 |
348 | return name.lower().encode("ascii")
349 |
350 |
351 | def _encode_target(target: str) -> str:
352 | """Percent-encodes a request target so that there are no invalid characters
353 |
354 | Pre-condition for this function is that 'target' must start with '/'.
355 | If that is the case then _TARGET_RE will always produce a match.
356 | """
357 | match = _TARGET_RE.match(target)
358 | if not match: # Defensive:
359 | raise LocationParseError(f"{target!r} is not a valid request URI")
360 |
361 | path, query = match.groups()
362 | encoded_target = _encode_invalid_chars(path, _PATH_CHARS)
363 | if query is not None:
364 | query = _encode_invalid_chars(query, _QUERY_CHARS)
365 | encoded_target += "?" + query
366 | return encoded_target
367 |
368 |
369 | def parse_url(url: str) -> Url:
370 | """
371 | Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is
372 | performed to parse incomplete urls. Fields not provided will be None.
373 | This parser is RFC 3986 and RFC 6874 compliant.
374 |
375 | The parser logic and helper functions are based heavily on
376 | work done in the ``rfc3986`` module.
377 |
378 | :param str url: URL to parse into a :class:`.Url` namedtuple.
379 |
380 | Partly backwards-compatible with :mod:`urllib.parse`.
381 |
382 | Example:
383 |
384 | .. code-block:: python
385 |
386 | import urllib3
387 |
388 | print( urllib3.util.parse_url('http://google.com/mail/'))
389 | # Url(scheme='http', host='google.com', port=None, path='/mail/', ...)
390 |
391 | print( urllib3.util.parse_url('google.com:80'))
392 | # Url(scheme=None, host='google.com', port=80, path=None, ...)
393 |
394 | print( urllib3.util.parse_url('/foo?bar'))
395 | # Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...)
396 | """
397 | if not url:
398 | # Empty
399 | return Url()
400 |
401 | source_url = url
402 | if not _SCHEME_RE.search(url):
403 | url = "//" + url
404 |
405 | scheme: str | None
406 | authority: str | None
407 | auth: str | None
408 | host: str | None
409 | port: str | None
410 | port_int: int | None
411 | path: str | None
412 | query: str | None
413 | fragment: str | None
414 |
415 | try:
416 | scheme, authority, path, query, fragment = _URI_RE.match(url).groups() # type: ignore[union-attr]
417 | normalize_uri = scheme is None or scheme.lower() in _NORMALIZABLE_SCHEMES
418 |
419 | if scheme:
420 | scheme = scheme.lower()
421 |
422 | if authority:
423 | auth, _, host_port = authority.rpartition("@")
424 | auth = auth or None
425 | host, port = _HOST_PORT_RE.match(host_port).groups() # type: ignore[union-attr]
426 | if auth and normalize_uri:
427 | auth = _encode_invalid_chars(auth, _USERINFO_CHARS)
428 | if port == "":
429 | port = None
430 | else:
431 | auth, host, port = None, None, None
432 |
433 | if port is not None:
434 | port_int = int(port)
435 | if not (0 <= port_int <= 65535):
436 | raise LocationParseError(url)
437 | else:
438 | port_int = None
439 |
440 | host = _normalize_host(host, scheme)
441 |
442 | if normalize_uri and path:
443 | path = _remove_path_dot_segments(path)
444 | path = _encode_invalid_chars(path, _PATH_CHARS)
445 | if normalize_uri and query:
446 | query = _encode_invalid_chars(query, _QUERY_CHARS)
447 | if normalize_uri and fragment:
448 | fragment = _encode_invalid_chars(fragment, _FRAGMENT_CHARS)
449 |
450 | except (ValueError, AttributeError) as e:
451 | raise LocationParseError(source_url) from e
452 |
453 | # For the sake of backwards compatibility we put empty
454 | # string values for path if there are any defined values
455 | # beyond the path in the URL.
456 | # TODO: Remove this when we break backwards compatibility.
457 | if not path:
458 | if query is not None or fragment is not None:
459 | path = ""
460 | else:
461 | path = None
462 |
463 | return Url(
464 | scheme=scheme,
465 | auth=auth,
466 | host=host,
467 | port=port_int,
468 | path=path,
469 | query=query,
470 | fragment=fragment,
471 | )
472 |
```