This is page 69 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/charset_normalizer/md.py:
--------------------------------------------------------------------------------
```python
1 | from functools import lru_cache
2 | from logging import getLogger
3 | from typing import List, Optional
4 |
5 | from .constant import (
6 | COMMON_SAFE_ASCII_CHARACTERS,
7 | TRACE,
8 | UNICODE_SECONDARY_RANGE_KEYWORD,
9 | )
10 | from .utils import (
11 | is_accentuated,
12 | is_arabic,
13 | is_arabic_isolated_form,
14 | is_case_variable,
15 | is_cjk,
16 | is_emoticon,
17 | is_hangul,
18 | is_hiragana,
19 | is_katakana,
20 | is_latin,
21 | is_punctuation,
22 | is_separator,
23 | is_symbol,
24 | is_thai,
25 | is_unprintable,
26 | remove_accent,
27 | unicode_range,
28 | )
29 |
30 |
31 | class MessDetectorPlugin:
32 | """
33 | Base abstract class used for mess detection plugins.
34 | All detectors MUST extend and implement given methods.
35 | """
36 |
37 | def eligible(self, character: str) -> bool:
38 | """
39 | Determine if given character should be fed in.
40 | """
41 | raise NotImplementedError # pragma: nocover
42 |
43 | def feed(self, character: str) -> None:
44 | """
45 | The main routine to be executed upon character.
46 | Insert the logic in witch the text would be considered chaotic.
47 | """
48 | raise NotImplementedError # pragma: nocover
49 |
50 | def reset(self) -> None: # pragma: no cover
51 | """
52 | Permit to reset the plugin to the initial state.
53 | """
54 | raise NotImplementedError
55 |
56 | @property
57 | def ratio(self) -> float:
58 | """
59 | Compute the chaos ratio based on what your feed() has seen.
60 | Must NOT be lower than 0.; No restriction gt 0.
61 | """
62 | raise NotImplementedError # pragma: nocover
63 |
64 |
65 | class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin):
66 | def __init__(self) -> None:
67 | self._punctuation_count: int = 0
68 | self._symbol_count: int = 0
69 | self._character_count: int = 0
70 |
71 | self._last_printable_char: Optional[str] = None
72 | self._frenzy_symbol_in_word: bool = False
73 |
74 | def eligible(self, character: str) -> bool:
75 | return character.isprintable()
76 |
77 | def feed(self, character: str) -> None:
78 | self._character_count += 1
79 |
80 | if (
81 | character != self._last_printable_char
82 | and character not in COMMON_SAFE_ASCII_CHARACTERS
83 | ):
84 | if is_punctuation(character):
85 | self._punctuation_count += 1
86 | elif (
87 | character.isdigit() is False
88 | and is_symbol(character)
89 | and is_emoticon(character) is False
90 | ):
91 | self._symbol_count += 2
92 |
93 | self._last_printable_char = character
94 |
95 | def reset(self) -> None: # pragma: no cover
96 | self._punctuation_count = 0
97 | self._character_count = 0
98 | self._symbol_count = 0
99 |
100 | @property
101 | def ratio(self) -> float:
102 | if self._character_count == 0:
103 | return 0.0
104 |
105 | ratio_of_punctuation: float = (
106 | self._punctuation_count + self._symbol_count
107 | ) / self._character_count
108 |
109 | return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0
110 |
111 |
112 | class TooManyAccentuatedPlugin(MessDetectorPlugin):
113 | def __init__(self) -> None:
114 | self._character_count: int = 0
115 | self._accentuated_count: int = 0
116 |
117 | def eligible(self, character: str) -> bool:
118 | return character.isalpha()
119 |
120 | def feed(self, character: str) -> None:
121 | self._character_count += 1
122 |
123 | if is_accentuated(character):
124 | self._accentuated_count += 1
125 |
126 | def reset(self) -> None: # pragma: no cover
127 | self._character_count = 0
128 | self._accentuated_count = 0
129 |
130 | @property
131 | def ratio(self) -> float:
132 | if self._character_count < 8:
133 | return 0.0
134 |
135 | ratio_of_accentuation: float = self._accentuated_count / self._character_count
136 | return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0
137 |
138 |
139 | class UnprintablePlugin(MessDetectorPlugin):
140 | def __init__(self) -> None:
141 | self._unprintable_count: int = 0
142 | self._character_count: int = 0
143 |
144 | def eligible(self, character: str) -> bool:
145 | return True
146 |
147 | def feed(self, character: str) -> None:
148 | if is_unprintable(character):
149 | self._unprintable_count += 1
150 | self._character_count += 1
151 |
152 | def reset(self) -> None: # pragma: no cover
153 | self._unprintable_count = 0
154 |
155 | @property
156 | def ratio(self) -> float:
157 | if self._character_count == 0:
158 | return 0.0
159 |
160 | return (self._unprintable_count * 8) / self._character_count
161 |
162 |
163 | class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin):
164 | def __init__(self) -> None:
165 | self._successive_count: int = 0
166 | self._character_count: int = 0
167 |
168 | self._last_latin_character: Optional[str] = None
169 |
170 | def eligible(self, character: str) -> bool:
171 | return character.isalpha() and is_latin(character)
172 |
173 | def feed(self, character: str) -> None:
174 | self._character_count += 1
175 | if (
176 | self._last_latin_character is not None
177 | and is_accentuated(character)
178 | and is_accentuated(self._last_latin_character)
179 | ):
180 | if character.isupper() and self._last_latin_character.isupper():
181 | self._successive_count += 1
182 | # Worse if its the same char duplicated with different accent.
183 | if remove_accent(character) == remove_accent(self._last_latin_character):
184 | self._successive_count += 1
185 | self._last_latin_character = character
186 |
187 | def reset(self) -> None: # pragma: no cover
188 | self._successive_count = 0
189 | self._character_count = 0
190 | self._last_latin_character = None
191 |
192 | @property
193 | def ratio(self) -> float:
194 | if self._character_count == 0:
195 | return 0.0
196 |
197 | return (self._successive_count * 2) / self._character_count
198 |
199 |
200 | class SuspiciousRange(MessDetectorPlugin):
201 | def __init__(self) -> None:
202 | self._suspicious_successive_range_count: int = 0
203 | self._character_count: int = 0
204 | self._last_printable_seen: Optional[str] = None
205 |
206 | def eligible(self, character: str) -> bool:
207 | return character.isprintable()
208 |
209 | def feed(self, character: str) -> None:
210 | self._character_count += 1
211 |
212 | if (
213 | character.isspace()
214 | or is_punctuation(character)
215 | or character in COMMON_SAFE_ASCII_CHARACTERS
216 | ):
217 | self._last_printable_seen = None
218 | return
219 |
220 | if self._last_printable_seen is None:
221 | self._last_printable_seen = character
222 | return
223 |
224 | unicode_range_a: Optional[str] = unicode_range(self._last_printable_seen)
225 | unicode_range_b: Optional[str] = unicode_range(character)
226 |
227 | if is_suspiciously_successive_range(unicode_range_a, unicode_range_b):
228 | self._suspicious_successive_range_count += 1
229 |
230 | self._last_printable_seen = character
231 |
232 | def reset(self) -> None: # pragma: no cover
233 | self._character_count = 0
234 | self._suspicious_successive_range_count = 0
235 | self._last_printable_seen = None
236 |
237 | @property
238 | def ratio(self) -> float:
239 | if self._character_count <= 13:
240 | return 0.0
241 |
242 | ratio_of_suspicious_range_usage: float = (
243 | self._suspicious_successive_range_count * 2
244 | ) / self._character_count
245 |
246 | return ratio_of_suspicious_range_usage
247 |
248 |
249 | class SuperWeirdWordPlugin(MessDetectorPlugin):
250 | def __init__(self) -> None:
251 | self._word_count: int = 0
252 | self._bad_word_count: int = 0
253 | self._foreign_long_count: int = 0
254 |
255 | self._is_current_word_bad: bool = False
256 | self._foreign_long_watch: bool = False
257 |
258 | self._character_count: int = 0
259 | self._bad_character_count: int = 0
260 |
261 | self._buffer: str = ""
262 | self._buffer_accent_count: int = 0
263 | self._buffer_glyph_count: int = 0
264 |
265 | def eligible(self, character: str) -> bool:
266 | return True
267 |
268 | def feed(self, character: str) -> None:
269 | if character.isalpha():
270 | self._buffer += character
271 | if is_accentuated(character):
272 | self._buffer_accent_count += 1
273 | if (
274 | self._foreign_long_watch is False
275 | and (is_latin(character) is False or is_accentuated(character))
276 | and is_cjk(character) is False
277 | and is_hangul(character) is False
278 | and is_katakana(character) is False
279 | and is_hiragana(character) is False
280 | and is_thai(character) is False
281 | ):
282 | self._foreign_long_watch = True
283 | if (
284 | is_cjk(character)
285 | or is_hangul(character)
286 | or is_katakana(character)
287 | or is_hiragana(character)
288 | or is_thai(character)
289 | ):
290 | self._buffer_glyph_count += 1
291 | return
292 | if not self._buffer:
293 | return
294 | if (
295 | character.isspace() or is_punctuation(character) or is_separator(character)
296 | ) and self._buffer:
297 | self._word_count += 1
298 | buffer_length: int = len(self._buffer)
299 |
300 | self._character_count += buffer_length
301 |
302 | if buffer_length >= 4:
303 | if self._buffer_accent_count / buffer_length >= 0.5:
304 | self._is_current_word_bad = True
305 | # Word/Buffer ending with an upper case accentuated letter are so rare,
306 | # that we will consider them all as suspicious. Same weight as foreign_long suspicious.
307 | elif (
308 | is_accentuated(self._buffer[-1])
309 | and self._buffer[-1].isupper()
310 | and all(_.isupper() for _ in self._buffer) is False
311 | ):
312 | self._foreign_long_count += 1
313 | self._is_current_word_bad = True
314 | elif self._buffer_glyph_count == 1:
315 | self._is_current_word_bad = True
316 | self._foreign_long_count += 1
317 | if buffer_length >= 24 and self._foreign_long_watch:
318 | camel_case_dst = [
319 | i
320 | for c, i in zip(self._buffer, range(0, buffer_length))
321 | if c.isupper()
322 | ]
323 | probable_camel_cased: bool = False
324 |
325 | if camel_case_dst and (len(camel_case_dst) / buffer_length <= 0.3):
326 | probable_camel_cased = True
327 |
328 | if not probable_camel_cased:
329 | self._foreign_long_count += 1
330 | self._is_current_word_bad = True
331 |
332 | if self._is_current_word_bad:
333 | self._bad_word_count += 1
334 | self._bad_character_count += len(self._buffer)
335 | self._is_current_word_bad = False
336 |
337 | self._foreign_long_watch = False
338 | self._buffer = ""
339 | self._buffer_accent_count = 0
340 | self._buffer_glyph_count = 0
341 | elif (
342 | character not in {"<", ">", "-", "=", "~", "|", "_"}
343 | and character.isdigit() is False
344 | and is_symbol(character)
345 | ):
346 | self._is_current_word_bad = True
347 | self._buffer += character
348 |
349 | def reset(self) -> None: # pragma: no cover
350 | self._buffer = ""
351 | self._is_current_word_bad = False
352 | self._foreign_long_watch = False
353 | self._bad_word_count = 0
354 | self._word_count = 0
355 | self._character_count = 0
356 | self._bad_character_count = 0
357 | self._foreign_long_count = 0
358 |
359 | @property
360 | def ratio(self) -> float:
361 | if self._word_count <= 10 and self._foreign_long_count == 0:
362 | return 0.0
363 |
364 | return self._bad_character_count / self._character_count
365 |
366 |
367 | class CjkInvalidStopPlugin(MessDetectorPlugin):
368 | """
369 | GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and
370 | can be easily detected. Searching for the overuse of '丅' and '丄'.
371 | """
372 |
373 | def __init__(self) -> None:
374 | self._wrong_stop_count: int = 0
375 | self._cjk_character_count: int = 0
376 |
377 | def eligible(self, character: str) -> bool:
378 | return True
379 |
380 | def feed(self, character: str) -> None:
381 | if character in {"丅", "丄"}:
382 | self._wrong_stop_count += 1
383 | return
384 | if is_cjk(character):
385 | self._cjk_character_count += 1
386 |
387 | def reset(self) -> None: # pragma: no cover
388 | self._wrong_stop_count = 0
389 | self._cjk_character_count = 0
390 |
391 | @property
392 | def ratio(self) -> float:
393 | if self._cjk_character_count < 16:
394 | return 0.0
395 | return self._wrong_stop_count / self._cjk_character_count
396 |
397 |
398 | class ArchaicUpperLowerPlugin(MessDetectorPlugin):
399 | def __init__(self) -> None:
400 | self._buf: bool = False
401 |
402 | self._character_count_since_last_sep: int = 0
403 |
404 | self._successive_upper_lower_count: int = 0
405 | self._successive_upper_lower_count_final: int = 0
406 |
407 | self._character_count: int = 0
408 |
409 | self._last_alpha_seen: Optional[str] = None
410 | self._current_ascii_only: bool = True
411 |
412 | def eligible(self, character: str) -> bool:
413 | return True
414 |
415 | def feed(self, character: str) -> None:
416 | is_concerned = character.isalpha() and is_case_variable(character)
417 | chunk_sep = is_concerned is False
418 |
419 | if chunk_sep and self._character_count_since_last_sep > 0:
420 | if (
421 | self._character_count_since_last_sep <= 64
422 | and character.isdigit() is False
423 | and self._current_ascii_only is False
424 | ):
425 | self._successive_upper_lower_count_final += (
426 | self._successive_upper_lower_count
427 | )
428 |
429 | self._successive_upper_lower_count = 0
430 | self._character_count_since_last_sep = 0
431 | self._last_alpha_seen = None
432 | self._buf = False
433 | self._character_count += 1
434 | self._current_ascii_only = True
435 |
436 | return
437 |
438 | if self._current_ascii_only is True and character.isascii() is False:
439 | self._current_ascii_only = False
440 |
441 | if self._last_alpha_seen is not None:
442 | if (character.isupper() and self._last_alpha_seen.islower()) or (
443 | character.islower() and self._last_alpha_seen.isupper()
444 | ):
445 | if self._buf is True:
446 | self._successive_upper_lower_count += 2
447 | self._buf = False
448 | else:
449 | self._buf = True
450 | else:
451 | self._buf = False
452 |
453 | self._character_count += 1
454 | self._character_count_since_last_sep += 1
455 | self._last_alpha_seen = character
456 |
457 | def reset(self) -> None: # pragma: no cover
458 | self._character_count = 0
459 | self._character_count_since_last_sep = 0
460 | self._successive_upper_lower_count = 0
461 | self._successive_upper_lower_count_final = 0
462 | self._last_alpha_seen = None
463 | self._buf = False
464 | self._current_ascii_only = True
465 |
466 | @property
467 | def ratio(self) -> float:
468 | if self._character_count == 0:
469 | return 0.0
470 |
471 | return self._successive_upper_lower_count_final / self._character_count
472 |
473 |
474 | class ArabicIsolatedFormPlugin(MessDetectorPlugin):
475 | def __init__(self) -> None:
476 | self._character_count: int = 0
477 | self._isolated_form_count: int = 0
478 |
479 | def reset(self) -> None: # pragma: no cover
480 | self._character_count = 0
481 | self._isolated_form_count = 0
482 |
483 | def eligible(self, character: str) -> bool:
484 | return is_arabic(character)
485 |
486 | def feed(self, character: str) -> None:
487 | self._character_count += 1
488 |
489 | if is_arabic_isolated_form(character):
490 | self._isolated_form_count += 1
491 |
492 | @property
493 | def ratio(self) -> float:
494 | if self._character_count < 8:
495 | return 0.0
496 |
497 | isolated_form_usage: float = self._isolated_form_count / self._character_count
498 |
499 | return isolated_form_usage
500 |
501 |
502 | @lru_cache(maxsize=1024)
503 | def is_suspiciously_successive_range(
504 | unicode_range_a: Optional[str], unicode_range_b: Optional[str]
505 | ) -> bool:
506 | """
507 | Determine if two Unicode range seen next to each other can be considered as suspicious.
508 | """
509 | if unicode_range_a is None or unicode_range_b is None:
510 | return True
511 |
512 | if unicode_range_a == unicode_range_b:
513 | return False
514 |
515 | if "Latin" in unicode_range_a and "Latin" in unicode_range_b:
516 | return False
517 |
518 | if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b:
519 | return False
520 |
521 | # Latin characters can be accompanied with a combining diacritical mark
522 | # eg. Vietnamese.
523 | if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and (
524 | "Combining" in unicode_range_a or "Combining" in unicode_range_b
525 | ):
526 | return False
527 |
528 | keywords_range_a, keywords_range_b = unicode_range_a.split(
529 | " "
530 | ), unicode_range_b.split(" ")
531 |
532 | for el in keywords_range_a:
533 | if el in UNICODE_SECONDARY_RANGE_KEYWORD:
534 | continue
535 | if el in keywords_range_b:
536 | return False
537 |
538 | # Japanese Exception
539 | range_a_jp_chars, range_b_jp_chars = (
540 | unicode_range_a
541 | in (
542 | "Hiragana",
543 | "Katakana",
544 | ),
545 | unicode_range_b in ("Hiragana", "Katakana"),
546 | )
547 | if (range_a_jp_chars or range_b_jp_chars) and (
548 | "CJK" in unicode_range_a or "CJK" in unicode_range_b
549 | ):
550 | return False
551 | if range_a_jp_chars and range_b_jp_chars:
552 | return False
553 |
554 | if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b:
555 | if "CJK" in unicode_range_a or "CJK" in unicode_range_b:
556 | return False
557 | if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin":
558 | return False
559 |
560 | # Chinese/Japanese use dedicated range for punctuation and/or separators.
561 | if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or (
562 | unicode_range_a in ["Katakana", "Hiragana"]
563 | and unicode_range_b in ["Katakana", "Hiragana"]
564 | ):
565 | if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b:
566 | return False
567 | if "Forms" in unicode_range_a or "Forms" in unicode_range_b:
568 | return False
569 | if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin":
570 | return False
571 |
572 | return True
573 |
574 |
575 | @lru_cache(maxsize=2048)
576 | def mess_ratio(
577 | decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False
578 | ) -> float:
579 | """
580 | Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier.
581 | """
582 |
583 | detectors: List[MessDetectorPlugin] = [
584 | md_class() for md_class in MessDetectorPlugin.__subclasses__()
585 | ]
586 |
587 | length: int = len(decoded_sequence) + 1
588 |
589 | mean_mess_ratio: float = 0.0
590 |
591 | if length < 512:
592 | intermediary_mean_mess_ratio_calc: int = 32
593 | elif length <= 1024:
594 | intermediary_mean_mess_ratio_calc = 64
595 | else:
596 | intermediary_mean_mess_ratio_calc = 128
597 |
598 | for character, index in zip(decoded_sequence + "\n", range(length)):
599 | for detector in detectors:
600 | if detector.eligible(character):
601 | detector.feed(character)
602 |
603 | if (
604 | index > 0 and index % intermediary_mean_mess_ratio_calc == 0
605 | ) or index == length - 1:
606 | mean_mess_ratio = sum(dt.ratio for dt in detectors)
607 |
608 | if mean_mess_ratio >= maximum_threshold:
609 | break
610 |
611 | if debug:
612 | logger = getLogger("charset_normalizer")
613 |
614 | logger.log(
615 | TRACE,
616 | "Mess-detector extended-analysis start. "
617 | f"intermediary_mean_mess_ratio_calc={intermediary_mean_mess_ratio_calc} mean_mess_ratio={mean_mess_ratio} "
618 | f"maximum_threshold={maximum_threshold}",
619 | )
620 |
621 | if len(decoded_sequence) > 16:
622 | logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}")
623 | logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}")
624 |
625 | for dt in detectors: # pragma: nocover
626 | logger.log(TRACE, f"{dt.__class__}: {dt.ratio}")
627 |
628 | return round(mean_mess_ratio, 3)
629 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/click/utils.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import re
3 | import sys
4 | import typing as t
5 | from functools import update_wrapper
6 | from types import ModuleType
7 | from types import TracebackType
8 |
9 | from ._compat import _default_text_stderr
10 | from ._compat import _default_text_stdout
11 | from ._compat import _find_binary_writer
12 | from ._compat import auto_wrap_for_ansi
13 | from ._compat import binary_streams
14 | from ._compat import open_stream
15 | from ._compat import should_strip_ansi
16 | from ._compat import strip_ansi
17 | from ._compat import text_streams
18 | from ._compat import WIN
19 | from .globals import resolve_color_default
20 |
21 | if t.TYPE_CHECKING:
22 | import typing_extensions as te
23 |
24 | P = te.ParamSpec("P")
25 |
26 | R = t.TypeVar("R")
27 |
28 |
29 | def _posixify(name: str) -> str:
30 | return "-".join(name.split()).lower()
31 |
32 |
33 | def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]":
34 | """Wraps a function so that it swallows exceptions."""
35 |
36 | def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]:
37 | try:
38 | return func(*args, **kwargs)
39 | except Exception:
40 | pass
41 | return None
42 |
43 | return update_wrapper(wrapper, func)
44 |
45 |
46 | def make_str(value: t.Any) -> str:
47 | """Converts a value into a valid string."""
48 | if isinstance(value, bytes):
49 | try:
50 | return value.decode(sys.getfilesystemencoding())
51 | except UnicodeError:
52 | return value.decode("utf-8", "replace")
53 | return str(value)
54 |
55 |
56 | def make_default_short_help(help: str, max_length: int = 45) -> str:
57 | """Returns a condensed version of help string."""
58 | # Consider only the first paragraph.
59 | paragraph_end = help.find("\n\n")
60 |
61 | if paragraph_end != -1:
62 | help = help[:paragraph_end]
63 |
64 | # Collapse newlines, tabs, and spaces.
65 | words = help.split()
66 |
67 | if not words:
68 | return ""
69 |
70 | # The first paragraph started with a "no rewrap" marker, ignore it.
71 | if words[0] == "\b":
72 | words = words[1:]
73 |
74 | total_length = 0
75 | last_index = len(words) - 1
76 |
77 | for i, word in enumerate(words):
78 | total_length += len(word) + (i > 0)
79 |
80 | if total_length > max_length: # too long, truncate
81 | break
82 |
83 | if word[-1] == ".": # sentence end, truncate without "..."
84 | return " ".join(words[: i + 1])
85 |
86 | if total_length == max_length and i != last_index:
87 | break # not at sentence end, truncate with "..."
88 | else:
89 | return " ".join(words) # no truncation needed
90 |
91 | # Account for the length of the suffix.
92 | total_length += len("...")
93 |
94 | # remove words until the length is short enough
95 | while i > 0:
96 | total_length -= len(words[i]) + (i > 0)
97 |
98 | if total_length <= max_length:
99 | break
100 |
101 | i -= 1
102 |
103 | return " ".join(words[:i]) + "..."
104 |
105 |
106 | class LazyFile:
107 | """A lazy file works like a regular file but it does not fully open
108 | the file but it does perform some basic checks early to see if the
109 | filename parameter does make sense. This is useful for safely opening
110 | files for writing.
111 | """
112 |
113 | def __init__(
114 | self,
115 | filename: t.Union[str, "os.PathLike[str]"],
116 | mode: str = "r",
117 | encoding: t.Optional[str] = None,
118 | errors: t.Optional[str] = "strict",
119 | atomic: bool = False,
120 | ):
121 | self.name: str = os.fspath(filename)
122 | self.mode = mode
123 | self.encoding = encoding
124 | self.errors = errors
125 | self.atomic = atomic
126 | self._f: t.Optional[t.IO[t.Any]]
127 | self.should_close: bool
128 |
129 | if self.name == "-":
130 | self._f, self.should_close = open_stream(filename, mode, encoding, errors)
131 | else:
132 | if "r" in mode:
133 | # Open and close the file in case we're opening it for
134 | # reading so that we can catch at least some errors in
135 | # some cases early.
136 | open(filename, mode).close()
137 | self._f = None
138 | self.should_close = True
139 |
140 | def __getattr__(self, name: str) -> t.Any:
141 | return getattr(self.open(), name)
142 |
143 | def __repr__(self) -> str:
144 | if self._f is not None:
145 | return repr(self._f)
146 | return f"<unopened file '{format_filename(self.name)}' {self.mode}>"
147 |
148 | def open(self) -> t.IO[t.Any]:
149 | """Opens the file if it's not yet open. This call might fail with
150 | a :exc:`FileError`. Not handling this error will produce an error
151 | that Click shows.
152 | """
153 | if self._f is not None:
154 | return self._f
155 | try:
156 | rv, self.should_close = open_stream(
157 | self.name, self.mode, self.encoding, self.errors, atomic=self.atomic
158 | )
159 | except OSError as e: # noqa: E402
160 | from .exceptions import FileError
161 |
162 | raise FileError(self.name, hint=e.strerror) from e
163 | self._f = rv
164 | return rv
165 |
166 | def close(self) -> None:
167 | """Closes the underlying file, no matter what."""
168 | if self._f is not None:
169 | self._f.close()
170 |
171 | def close_intelligently(self) -> None:
172 | """This function only closes the file if it was opened by the lazy
173 | file wrapper. For instance this will never close stdin.
174 | """
175 | if self.should_close:
176 | self.close()
177 |
178 | def __enter__(self) -> "LazyFile":
179 | return self
180 |
181 | def __exit__(
182 | self,
183 | exc_type: t.Optional[t.Type[BaseException]],
184 | exc_value: t.Optional[BaseException],
185 | tb: t.Optional[TracebackType],
186 | ) -> None:
187 | self.close_intelligently()
188 |
189 | def __iter__(self) -> t.Iterator[t.AnyStr]:
190 | self.open()
191 | return iter(self._f) # type: ignore
192 |
193 |
194 | class KeepOpenFile:
195 | def __init__(self, file: t.IO[t.Any]) -> None:
196 | self._file: t.IO[t.Any] = file
197 |
198 | def __getattr__(self, name: str) -> t.Any:
199 | return getattr(self._file, name)
200 |
201 | def __enter__(self) -> "KeepOpenFile":
202 | return self
203 |
204 | def __exit__(
205 | self,
206 | exc_type: t.Optional[t.Type[BaseException]],
207 | exc_value: t.Optional[BaseException],
208 | tb: t.Optional[TracebackType],
209 | ) -> None:
210 | pass
211 |
212 | def __repr__(self) -> str:
213 | return repr(self._file)
214 |
215 | def __iter__(self) -> t.Iterator[t.AnyStr]:
216 | return iter(self._file)
217 |
218 |
219 | def echo(
220 | message: t.Optional[t.Any] = None,
221 | file: t.Optional[t.IO[t.Any]] = None,
222 | nl: bool = True,
223 | err: bool = False,
224 | color: t.Optional[bool] = None,
225 | ) -> None:
226 | """Print a message and newline to stdout or a file. This should be
227 | used instead of :func:`print` because it provides better support
228 | for different data, files, and environments.
229 |
230 | Compared to :func:`print`, this does the following:
231 |
232 | - Ensures that the output encoding is not misconfigured on Linux.
233 | - Supports Unicode in the Windows console.
234 | - Supports writing to binary outputs, and supports writing bytes
235 | to text outputs.
236 | - Supports colors and styles on Windows.
237 | - Removes ANSI color and style codes if the output does not look
238 | like an interactive terminal.
239 | - Always flushes the output.
240 |
241 | :param message: The string or bytes to output. Other objects are
242 | converted to strings.
243 | :param file: The file to write to. Defaults to ``stdout``.
244 | :param err: Write to ``stderr`` instead of ``stdout``.
245 | :param nl: Print a newline after the message. Enabled by default.
246 | :param color: Force showing or hiding colors and other styles. By
247 | default Click will remove color if the output does not look like
248 | an interactive terminal.
249 |
250 | .. versionchanged:: 6.0
251 | Support Unicode output on the Windows console. Click does not
252 | modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()``
253 | will still not support Unicode.
254 |
255 | .. versionchanged:: 4.0
256 | Added the ``color`` parameter.
257 |
258 | .. versionadded:: 3.0
259 | Added the ``err`` parameter.
260 |
261 | .. versionchanged:: 2.0
262 | Support colors on Windows if colorama is installed.
263 | """
264 | if file is None:
265 | if err:
266 | file = _default_text_stderr()
267 | else:
268 | file = _default_text_stdout()
269 |
270 | # There are no standard streams attached to write to. For example,
271 | # pythonw on Windows.
272 | if file is None:
273 | return
274 |
275 | # Convert non bytes/text into the native string type.
276 | if message is not None and not isinstance(message, (str, bytes, bytearray)):
277 | out: t.Optional[t.Union[str, bytes]] = str(message)
278 | else:
279 | out = message
280 |
281 | if nl:
282 | out = out or ""
283 | if isinstance(out, str):
284 | out += "\n"
285 | else:
286 | out += b"\n"
287 |
288 | if not out:
289 | file.flush()
290 | return
291 |
292 | # If there is a message and the value looks like bytes, we manually
293 | # need to find the binary stream and write the message in there.
294 | # This is done separately so that most stream types will work as you
295 | # would expect. Eg: you can write to StringIO for other cases.
296 | if isinstance(out, (bytes, bytearray)):
297 | binary_file = _find_binary_writer(file)
298 |
299 | if binary_file is not None:
300 | file.flush()
301 | binary_file.write(out)
302 | binary_file.flush()
303 | return
304 |
305 | # ANSI style code support. For no message or bytes, nothing happens.
306 | # When outputting to a file instead of a terminal, strip codes.
307 | else:
308 | color = resolve_color_default(color)
309 |
310 | if should_strip_ansi(file, color):
311 | out = strip_ansi(out)
312 | elif WIN:
313 | if auto_wrap_for_ansi is not None:
314 | file = auto_wrap_for_ansi(file) # type: ignore
315 | elif not color:
316 | out = strip_ansi(out)
317 |
318 | file.write(out) # type: ignore
319 | file.flush()
320 |
321 |
322 | def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO:
323 | """Returns a system stream for byte processing.
324 |
325 | :param name: the name of the stream to open. Valid names are ``'stdin'``,
326 | ``'stdout'`` and ``'stderr'``
327 | """
328 | opener = binary_streams.get(name)
329 | if opener is None:
330 | raise TypeError(f"Unknown standard stream '{name}'")
331 | return opener()
332 |
333 |
334 | def get_text_stream(
335 | name: "te.Literal['stdin', 'stdout', 'stderr']",
336 | encoding: t.Optional[str] = None,
337 | errors: t.Optional[str] = "strict",
338 | ) -> t.TextIO:
339 | """Returns a system stream for text processing. This usually returns
340 | a wrapped stream around a binary stream returned from
341 | :func:`get_binary_stream` but it also can take shortcuts for already
342 | correctly configured streams.
343 |
344 | :param name: the name of the stream to open. Valid names are ``'stdin'``,
345 | ``'stdout'`` and ``'stderr'``
346 | :param encoding: overrides the detected default encoding.
347 | :param errors: overrides the default error mode.
348 | """
349 | opener = text_streams.get(name)
350 | if opener is None:
351 | raise TypeError(f"Unknown standard stream '{name}'")
352 | return opener(encoding, errors)
353 |
354 |
355 | def open_file(
356 | filename: str,
357 | mode: str = "r",
358 | encoding: t.Optional[str] = None,
359 | errors: t.Optional[str] = "strict",
360 | lazy: bool = False,
361 | atomic: bool = False,
362 | ) -> t.IO[t.Any]:
363 | """Open a file, with extra behavior to handle ``'-'`` to indicate
364 | a standard stream, lazy open on write, and atomic write. Similar to
365 | the behavior of the :class:`~click.File` param type.
366 |
367 | If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is
368 | wrapped so that using it in a context manager will not close it.
369 | This makes it possible to use the function without accidentally
370 | closing a standard stream:
371 |
372 | .. code-block:: python
373 |
374 | with open_file(filename) as f:
375 | ...
376 |
377 | :param filename: The name of the file to open, or ``'-'`` for
378 | ``stdin``/``stdout``.
379 | :param mode: The mode in which to open the file.
380 | :param encoding: The encoding to decode or encode a file opened in
381 | text mode.
382 | :param errors: The error handling mode.
383 | :param lazy: Wait to open the file until it is accessed. For read
384 | mode, the file is temporarily opened to raise access errors
385 | early, then closed until it is read again.
386 | :param atomic: Write to a temporary file and replace the given file
387 | on close.
388 |
389 | .. versionadded:: 3.0
390 | """
391 | if lazy:
392 | return t.cast(
393 | t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic)
394 | )
395 |
396 | f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)
397 |
398 | if not should_close:
399 | f = t.cast(t.IO[t.Any], KeepOpenFile(f))
400 |
401 | return f
402 |
403 |
404 | def format_filename(
405 | filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]",
406 | shorten: bool = False,
407 | ) -> str:
408 | """Format a filename as a string for display. Ensures the filename can be
409 | displayed by replacing any invalid bytes or surrogate escapes in the name
410 | with the replacement character ``�``.
411 |
412 | Invalid bytes or surrogate escapes will raise an error when written to a
413 | stream with ``errors="strict". This will typically happen with ``stdout``
414 | when the locale is something like ``en_GB.UTF-8``.
415 |
416 | Many scenarios *are* safe to write surrogates though, due to PEP 538 and
417 | PEP 540, including:
418 |
419 | - Writing to ``stderr``, which uses ``errors="backslashreplace"``.
420 | - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens
421 | stdout and stderr with ``errors="surrogateescape"``.
422 | - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``.
423 | - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``.
424 | Python opens stdout and stderr with ``errors="surrogateescape"``.
425 |
426 | :param filename: formats a filename for UI display. This will also convert
427 | the filename into unicode without failing.
428 | :param shorten: this optionally shortens the filename to strip of the
429 | path that leads up to it.
430 | """
431 | if shorten:
432 | filename = os.path.basename(filename)
433 | else:
434 | filename = os.fspath(filename)
435 |
436 | if isinstance(filename, bytes):
437 | filename = filename.decode(sys.getfilesystemencoding(), "replace")
438 | else:
439 | filename = filename.encode("utf-8", "surrogateescape").decode(
440 | "utf-8", "replace"
441 | )
442 |
443 | return filename
444 |
445 |
446 | def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str:
447 | r"""Returns the config folder for the application. The default behavior
448 | is to return whatever is most appropriate for the operating system.
449 |
450 | To give you an idea, for an app called ``"Foo Bar"``, something like
451 | the following folders could be returned:
452 |
453 | Mac OS X:
454 | ``~/Library/Application Support/Foo Bar``
455 | Mac OS X (POSIX):
456 | ``~/.foo-bar``
457 | Unix:
458 | ``~/.config/foo-bar``
459 | Unix (POSIX):
460 | ``~/.foo-bar``
461 | Windows (roaming):
462 | ``C:\Users\<user>\AppData\Roaming\Foo Bar``
463 | Windows (not roaming):
464 | ``C:\Users\<user>\AppData\Local\Foo Bar``
465 |
466 | .. versionadded:: 2.0
467 |
468 | :param app_name: the application name. This should be properly capitalized
469 | and can contain whitespace.
470 | :param roaming: controls if the folder should be roaming or not on Windows.
471 | Has no effect otherwise.
472 | :param force_posix: if this is set to `True` then on any POSIX system the
473 | folder will be stored in the home folder with a leading
474 | dot instead of the XDG config home or darwin's
475 | application support folder.
476 | """
477 | if WIN:
478 | key = "APPDATA" if roaming else "LOCALAPPDATA"
479 | folder = os.environ.get(key)
480 | if folder is None:
481 | folder = os.path.expanduser("~")
482 | return os.path.join(folder, app_name)
483 | if force_posix:
484 | return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}"))
485 | if sys.platform == "darwin":
486 | return os.path.join(
487 | os.path.expanduser("~/Library/Application Support"), app_name
488 | )
489 | return os.path.join(
490 | os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")),
491 | _posixify(app_name),
492 | )
493 |
494 |
495 | class PacifyFlushWrapper:
496 | """This wrapper is used to catch and suppress BrokenPipeErrors resulting
497 | from ``.flush()`` being called on broken pipe during the shutdown/final-GC
498 | of the Python interpreter. Notably ``.flush()`` is always called on
499 | ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any
500 | other cleanup code, and the case where the underlying file is not a broken
501 | pipe, all calls and attributes are proxied.
502 | """
503 |
504 | def __init__(self, wrapped: t.IO[t.Any]) -> None:
505 | self.wrapped = wrapped
506 |
507 | def flush(self) -> None:
508 | try:
509 | self.wrapped.flush()
510 | except OSError as e:
511 | import errno
512 |
513 | if e.errno != errno.EPIPE:
514 | raise
515 |
516 | def __getattr__(self, attr: str) -> t.Any:
517 | return getattr(self.wrapped, attr)
518 |
519 |
520 | def _detect_program_name(
521 | path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None
522 | ) -> str:
523 | """Determine the command used to run the program, for use in help
524 | text. If a file or entry point was executed, the file name is
525 | returned. If ``python -m`` was used to execute a module or package,
526 | ``python -m name`` is returned.
527 |
528 | This doesn't try to be too precise, the goal is to give a concise
529 | name for help text. Files are only shown as their name without the
530 | path. ``python`` is only shown for modules, and the full path to
531 | ``sys.executable`` is not shown.
532 |
533 | :param path: The Python file being executed. Python puts this in
534 | ``sys.argv[0]``, which is used by default.
535 | :param _main: The ``__main__`` module. This should only be passed
536 | during internal testing.
537 |
538 | .. versionadded:: 8.0
539 | Based on command args detection in the Werkzeug reloader.
540 |
541 | :meta private:
542 | """
543 | if _main is None:
544 | _main = sys.modules["__main__"]
545 |
546 | if not path:
547 | path = sys.argv[0]
548 |
549 | # The value of __package__ indicates how Python was called. It may
550 | # not exist if a setuptools script is installed as an egg. It may be
551 | # set incorrectly for entry points created with pip on Windows.
552 | # It is set to "" inside a Shiv or PEX zipapp.
553 | if getattr(_main, "__package__", None) in {None, ""} or (
554 | os.name == "nt"
555 | and _main.__package__ == ""
556 | and not os.path.exists(path)
557 | and os.path.exists(f"{path}.exe")
558 | ):
559 | # Executed a file, like "python app.py".
560 | return os.path.basename(path)
561 |
562 | # Executed a module, like "python -m example".
563 | # Rewritten by Python from "-m script" to "/path/to/script.py".
564 | # Need to look at main module to determine how it was executed.
565 | py_module = t.cast(str, _main.__package__)
566 | name = os.path.splitext(os.path.basename(path))[0]
567 |
568 | # A submodule like "example.cli".
569 | if name != "__main__":
570 | py_module = f"{py_module}.{name}"
571 |
572 | return f"python -m {py_module.lstrip('.')}"
573 |
574 |
575 | def _expand_args(
576 | args: t.Iterable[str],
577 | *,
578 | user: bool = True,
579 | env: bool = True,
580 | glob_recursive: bool = True,
581 | ) -> t.List[str]:
582 | """Simulate Unix shell expansion with Python functions.
583 |
584 | See :func:`glob.glob`, :func:`os.path.expanduser`, and
585 | :func:`os.path.expandvars`.
586 |
587 | This is intended for use on Windows, where the shell does not do any
588 | expansion. It may not exactly match what a Unix shell would do.
589 |
590 | :param args: List of command line arguments to expand.
591 | :param user: Expand user home directory.
592 | :param env: Expand environment variables.
593 | :param glob_recursive: ``**`` matches directories recursively.
594 |
595 | .. versionchanged:: 8.1
596 | Invalid glob patterns are treated as empty expansions rather
597 | than raising an error.
598 |
599 | .. versionadded:: 8.0
600 |
601 | :meta private:
602 | """
603 | from glob import glob
604 |
605 | out = []
606 |
607 | for arg in args:
608 | if user:
609 | arg = os.path.expanduser(arg)
610 |
611 | if env:
612 | arg = os.path.expandvars(arg)
613 |
614 | try:
615 | matches = glob(arg, recursive=glob_recursive)
616 | except re.error:
617 | matches = []
618 |
619 | if not matches:
620 | out.append(arg)
621 | else:
622 | out.extend(matches)
623 |
624 | return out
625 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import absolute_import
2 |
3 | import datetime
4 | import logging
5 | import os
6 | import re
7 | import socket
8 | import warnings
9 | from socket import error as SocketError
10 | from socket import timeout as SocketTimeout
11 |
12 | from .packages import six
13 | from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection
14 | from .packages.six.moves.http_client import HTTPException # noqa: F401
15 | from .util.proxy import create_proxy_ssl_context
16 |
17 | try: # Compiled with SSL?
18 | import ssl
19 |
20 | BaseSSLError = ssl.SSLError
21 | except (ImportError, AttributeError): # Platform-specific: No SSL.
22 | ssl = None
23 |
24 | class BaseSSLError(BaseException):
25 | pass
26 |
27 |
28 | try:
29 | # Python 3: not a no-op, we're adding this to the namespace so it can be imported.
30 | ConnectionError = ConnectionError
31 | except NameError:
32 | # Python 2
33 | class ConnectionError(Exception):
34 | pass
35 |
36 |
37 | try: # Python 3:
38 | # Not a no-op, we're adding this to the namespace so it can be imported.
39 | BrokenPipeError = BrokenPipeError
40 | except NameError: # Python 2:
41 |
42 | class BrokenPipeError(Exception):
43 | pass
44 |
45 |
46 | from ._collections import HTTPHeaderDict # noqa (historical, removed in v2)
47 | from ._version import __version__
48 | from .exceptions import (
49 | ConnectTimeoutError,
50 | NewConnectionError,
51 | SubjectAltNameWarning,
52 | SystemTimeWarning,
53 | )
54 | from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection
55 | from .util.ssl_ import (
56 | assert_fingerprint,
57 | create_urllib3_context,
58 | is_ipaddress,
59 | resolve_cert_reqs,
60 | resolve_ssl_version,
61 | ssl_wrap_socket,
62 | )
63 | from .util.ssl_match_hostname import CertificateError, match_hostname
64 |
65 | log = logging.getLogger(__name__)
66 |
67 | port_by_scheme = {"http": 80, "https": 443}
68 |
69 | # When it comes time to update this value as a part of regular maintenance
70 | # (ie test_recent_date is failing) update it to ~6 months before the current date.
71 | RECENT_DATE = datetime.date(2022, 1, 1)
72 |
73 | _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
74 |
75 |
76 | class HTTPConnection(_HTTPConnection, object):
77 | """
78 | Based on :class:`http.client.HTTPConnection` but provides an extra constructor
79 | backwards-compatibility layer between older and newer Pythons.
80 |
81 | Additional keyword parameters are used to configure attributes of the connection.
82 | Accepted parameters include:
83 |
84 | - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool`
85 | - ``source_address``: Set the source address for the current connection.
86 | - ``socket_options``: Set specific options on the underlying socket. If not specified, then
87 | defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling
88 | Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy.
89 |
90 | For example, if you wish to enable TCP Keep Alive in addition to the defaults,
91 | you might pass:
92 |
93 | .. code-block:: python
94 |
95 | HTTPConnection.default_socket_options + [
96 | (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
97 | ]
98 |
99 | Or you may want to disable the defaults by passing an empty list (e.g., ``[]``).
100 | """
101 |
102 | default_port = port_by_scheme["http"]
103 |
104 | #: Disable Nagle's algorithm by default.
105 | #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]``
106 | default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
107 |
108 | #: Whether this connection verifies the host's certificate.
109 | is_verified = False
110 |
111 | #: Whether this proxy connection (if used) verifies the proxy host's
112 | #: certificate.
113 | proxy_is_verified = None
114 |
115 | def __init__(self, *args, **kw):
116 | if not six.PY2:
117 | kw.pop("strict", None)
118 |
119 | # Pre-set source_address.
120 | self.source_address = kw.get("source_address")
121 |
122 | #: The socket options provided by the user. If no options are
123 | #: provided, we use the default options.
124 | self.socket_options = kw.pop("socket_options", self.default_socket_options)
125 |
126 | # Proxy options provided by the user.
127 | self.proxy = kw.pop("proxy", None)
128 | self.proxy_config = kw.pop("proxy_config", None)
129 |
130 | _HTTPConnection.__init__(self, *args, **kw)
131 |
132 | @property
133 | def host(self):
134 | """
135 | Getter method to remove any trailing dots that indicate the hostname is an FQDN.
136 |
137 | In general, SSL certificates don't include the trailing dot indicating a
138 | fully-qualified domain name, and thus, they don't validate properly when
139 | checked against a domain name that includes the dot. In addition, some
140 | servers may not expect to receive the trailing dot when provided.
141 |
142 | However, the hostname with trailing dot is critical to DNS resolution; doing a
143 | lookup with the trailing dot will properly only resolve the appropriate FQDN,
144 | whereas a lookup without a trailing dot will search the system's search domain
145 | list. Thus, it's important to keep the original host around for use only in
146 | those cases where it's appropriate (i.e., when doing DNS lookup to establish the
147 | actual TCP connection across which we're going to send HTTP requests).
148 | """
149 | return self._dns_host.rstrip(".")
150 |
151 | @host.setter
152 | def host(self, value):
153 | """
154 | Setter for the `host` property.
155 |
156 | We assume that only urllib3 uses the _dns_host attribute; httplib itself
157 | only uses `host`, and it seems reasonable that other libraries follow suit.
158 | """
159 | self._dns_host = value
160 |
161 | def _new_conn(self):
162 | """Establish a socket connection and set nodelay settings on it.
163 |
164 | :return: New socket connection.
165 | """
166 | extra_kw = {}
167 | if self.source_address:
168 | extra_kw["source_address"] = self.source_address
169 |
170 | if self.socket_options:
171 | extra_kw["socket_options"] = self.socket_options
172 |
173 | try:
174 | conn = connection.create_connection(
175 | (self._dns_host, self.port), self.timeout, **extra_kw
176 | )
177 |
178 | except SocketTimeout:
179 | raise ConnectTimeoutError(
180 | self,
181 | "Connection to %s timed out. (connect timeout=%s)"
182 | % (self.host, self.timeout),
183 | )
184 |
185 | except SocketError as e:
186 | raise NewConnectionError(
187 | self, "Failed to establish a new connection: %s" % e
188 | )
189 |
190 | return conn
191 |
192 | def _is_using_tunnel(self):
193 | # Google App Engine's httplib does not define _tunnel_host
194 | return getattr(self, "_tunnel_host", None)
195 |
196 | def _prepare_conn(self, conn):
197 | self.sock = conn
198 | if self._is_using_tunnel():
199 | # TODO: Fix tunnel so it doesn't depend on self.sock state.
200 | self._tunnel()
201 | # Mark this connection as not reusable
202 | self.auto_open = 0
203 |
204 | def connect(self):
205 | conn = self._new_conn()
206 | self._prepare_conn(conn)
207 |
208 | def putrequest(self, method, url, *args, **kwargs):
209 | """ """
210 | # Empty docstring because the indentation of CPython's implementation
211 | # is broken but we don't want this method in our documentation.
212 | match = _CONTAINS_CONTROL_CHAR_RE.search(method)
213 | if match:
214 | raise ValueError(
215 | "Method cannot contain non-token characters %r (found at least %r)"
216 | % (method, match.group())
217 | )
218 |
219 | return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
220 |
221 | def putheader(self, header, *values):
222 | """ """
223 | if not any(isinstance(v, str) and v == SKIP_HEADER for v in values):
224 | _HTTPConnection.putheader(self, header, *values)
225 | elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS:
226 | raise ValueError(
227 | "urllib3.util.SKIP_HEADER only supports '%s'"
228 | % ("', '".join(map(str.title, sorted(SKIPPABLE_HEADERS))),)
229 | )
230 |
231 | def request(self, method, url, body=None, headers=None):
232 | # Update the inner socket's timeout value to send the request.
233 | # This only triggers if the connection is re-used.
234 | if getattr(self, "sock", None) is not None:
235 | self.sock.settimeout(self.timeout)
236 |
237 | if headers is None:
238 | headers = {}
239 | else:
240 | # Avoid modifying the headers passed into .request()
241 | headers = headers.copy()
242 | if "user-agent" not in (six.ensure_str(k.lower()) for k in headers):
243 | headers["User-Agent"] = _get_default_user_agent()
244 | super(HTTPConnection, self).request(method, url, body=body, headers=headers)
245 |
246 | def request_chunked(self, method, url, body=None, headers=None):
247 | """
248 | Alternative to the common request method, which sends the
249 | body with chunked encoding and not as one block
250 | """
251 | headers = headers or {}
252 | header_keys = set([six.ensure_str(k.lower()) for k in headers])
253 | skip_accept_encoding = "accept-encoding" in header_keys
254 | skip_host = "host" in header_keys
255 | self.putrequest(
256 | method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host
257 | )
258 | if "user-agent" not in header_keys:
259 | self.putheader("User-Agent", _get_default_user_agent())
260 | for header, value in headers.items():
261 | self.putheader(header, value)
262 | if "transfer-encoding" not in header_keys:
263 | self.putheader("Transfer-Encoding", "chunked")
264 | self.endheaders()
265 |
266 | if body is not None:
267 | stringish_types = six.string_types + (bytes,)
268 | if isinstance(body, stringish_types):
269 | body = (body,)
270 | for chunk in body:
271 | if not chunk:
272 | continue
273 | if not isinstance(chunk, bytes):
274 | chunk = chunk.encode("utf8")
275 | len_str = hex(len(chunk))[2:]
276 | to_send = bytearray(len_str.encode())
277 | to_send += b"\r\n"
278 | to_send += chunk
279 | to_send += b"\r\n"
280 | self.send(to_send)
281 |
282 | # After the if clause, to always have a closed body
283 | self.send(b"0\r\n\r\n")
284 |
285 |
286 | class HTTPSConnection(HTTPConnection):
287 | """
288 | Many of the parameters to this constructor are passed to the underlying SSL
289 | socket by means of :py:func:`urllib3.util.ssl_wrap_socket`.
290 | """
291 |
292 | default_port = port_by_scheme["https"]
293 |
294 | cert_reqs = None
295 | ca_certs = None
296 | ca_cert_dir = None
297 | ca_cert_data = None
298 | ssl_version = None
299 | assert_fingerprint = None
300 | tls_in_tls_required = False
301 |
302 | def __init__(
303 | self,
304 | host,
305 | port=None,
306 | key_file=None,
307 | cert_file=None,
308 | key_password=None,
309 | strict=None,
310 | timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
311 | ssl_context=None,
312 | server_hostname=None,
313 | **kw
314 | ):
315 |
316 | HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw)
317 |
318 | self.key_file = key_file
319 | self.cert_file = cert_file
320 | self.key_password = key_password
321 | self.ssl_context = ssl_context
322 | self.server_hostname = server_hostname
323 |
324 | # Required property for Google AppEngine 1.9.0 which otherwise causes
325 | # HTTPS requests to go out as HTTP. (See Issue #356)
326 | self._protocol = "https"
327 |
328 | def set_cert(
329 | self,
330 | key_file=None,
331 | cert_file=None,
332 | cert_reqs=None,
333 | key_password=None,
334 | ca_certs=None,
335 | assert_hostname=None,
336 | assert_fingerprint=None,
337 | ca_cert_dir=None,
338 | ca_cert_data=None,
339 | ):
340 | """
341 | This method should only be called once, before the connection is used.
342 | """
343 | # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also
344 | # have an SSLContext object in which case we'll use its verify_mode.
345 | if cert_reqs is None:
346 | if self.ssl_context is not None:
347 | cert_reqs = self.ssl_context.verify_mode
348 | else:
349 | cert_reqs = resolve_cert_reqs(None)
350 |
351 | self.key_file = key_file
352 | self.cert_file = cert_file
353 | self.cert_reqs = cert_reqs
354 | self.key_password = key_password
355 | self.assert_hostname = assert_hostname
356 | self.assert_fingerprint = assert_fingerprint
357 | self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
358 | self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
359 | self.ca_cert_data = ca_cert_data
360 |
361 | def connect(self):
362 | # Add certificate verification
363 | self.sock = conn = self._new_conn()
364 | hostname = self.host
365 | tls_in_tls = False
366 |
367 | if self._is_using_tunnel():
368 | if self.tls_in_tls_required:
369 | self.sock = conn = self._connect_tls_proxy(hostname, conn)
370 | tls_in_tls = True
371 |
372 | # Calls self._set_hostport(), so self.host is
373 | # self._tunnel_host below.
374 | self._tunnel()
375 | # Mark this connection as not reusable
376 | self.auto_open = 0
377 |
378 | # Override the host with the one we're requesting data from.
379 | hostname = self._tunnel_host
380 |
381 | server_hostname = hostname
382 | if self.server_hostname is not None:
383 | server_hostname = self.server_hostname
384 |
385 | is_time_off = datetime.date.today() < RECENT_DATE
386 | if is_time_off:
387 | warnings.warn(
388 | (
389 | "System time is way off (before {0}). This will probably "
390 | "lead to SSL verification errors"
391 | ).format(RECENT_DATE),
392 | SystemTimeWarning,
393 | )
394 |
395 | # Wrap socket using verification with the root certs in
396 | # trusted_root_certs
397 | default_ssl_context = False
398 | if self.ssl_context is None:
399 | default_ssl_context = True
400 | self.ssl_context = create_urllib3_context(
401 | ssl_version=resolve_ssl_version(self.ssl_version),
402 | cert_reqs=resolve_cert_reqs(self.cert_reqs),
403 | )
404 |
405 | context = self.ssl_context
406 | context.verify_mode = resolve_cert_reqs(self.cert_reqs)
407 |
408 | # Try to load OS default certs if none are given.
409 | # Works well on Windows (requires Python3.4+)
410 | if (
411 | not self.ca_certs
412 | and not self.ca_cert_dir
413 | and not self.ca_cert_data
414 | and default_ssl_context
415 | and hasattr(context, "load_default_certs")
416 | ):
417 | context.load_default_certs()
418 |
419 | self.sock = ssl_wrap_socket(
420 | sock=conn,
421 | keyfile=self.key_file,
422 | certfile=self.cert_file,
423 | key_password=self.key_password,
424 | ca_certs=self.ca_certs,
425 | ca_cert_dir=self.ca_cert_dir,
426 | ca_cert_data=self.ca_cert_data,
427 | server_hostname=server_hostname,
428 | ssl_context=context,
429 | tls_in_tls=tls_in_tls,
430 | )
431 |
432 | # If we're using all defaults and the connection
433 | # is TLSv1 or TLSv1.1 we throw a DeprecationWarning
434 | # for the host.
435 | if (
436 | default_ssl_context
437 | and self.ssl_version is None
438 | and hasattr(self.sock, "version")
439 | and self.sock.version() in {"TLSv1", "TLSv1.1"}
440 | ):
441 | warnings.warn(
442 | "Negotiating TLSv1/TLSv1.1 by default is deprecated "
443 | "and will be disabled in urllib3 v2.0.0. Connecting to "
444 | "'%s' with '%s' can be enabled by explicitly opting-in "
445 | "with 'ssl_version'" % (self.host, self.sock.version()),
446 | DeprecationWarning,
447 | )
448 |
449 | if self.assert_fingerprint:
450 | assert_fingerprint(
451 | self.sock.getpeercert(binary_form=True), self.assert_fingerprint
452 | )
453 | elif (
454 | context.verify_mode != ssl.CERT_NONE
455 | and not getattr(context, "check_hostname", False)
456 | and self.assert_hostname is not False
457 | ):
458 | # While urllib3 attempts to always turn off hostname matching from
459 | # the TLS library, this cannot always be done. So we check whether
460 | # the TLS Library still thinks it's matching hostnames.
461 | cert = self.sock.getpeercert()
462 | if not cert.get("subjectAltName", ()):
463 | warnings.warn(
464 | (
465 | "Certificate for {0} has no `subjectAltName`, falling back to check for a "
466 | "`commonName` for now. This feature is being removed by major browsers and "
467 | "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 "
468 | "for details.)".format(hostname)
469 | ),
470 | SubjectAltNameWarning,
471 | )
472 | _match_hostname(cert, self.assert_hostname or server_hostname)
473 |
474 | self.is_verified = (
475 | context.verify_mode == ssl.CERT_REQUIRED
476 | or self.assert_fingerprint is not None
477 | )
478 |
479 | def _connect_tls_proxy(self, hostname, conn):
480 | """
481 | Establish a TLS connection to the proxy using the provided SSL context.
482 | """
483 | proxy_config = self.proxy_config
484 | ssl_context = proxy_config.ssl_context
485 | if ssl_context:
486 | # If the user provided a proxy context, we assume CA and client
487 | # certificates have already been set
488 | return ssl_wrap_socket(
489 | sock=conn,
490 | server_hostname=hostname,
491 | ssl_context=ssl_context,
492 | )
493 |
494 | ssl_context = create_proxy_ssl_context(
495 | self.ssl_version,
496 | self.cert_reqs,
497 | self.ca_certs,
498 | self.ca_cert_dir,
499 | self.ca_cert_data,
500 | )
501 |
502 | # If no cert was provided, use only the default options for server
503 | # certificate validation
504 | socket = ssl_wrap_socket(
505 | sock=conn,
506 | ca_certs=self.ca_certs,
507 | ca_cert_dir=self.ca_cert_dir,
508 | ca_cert_data=self.ca_cert_data,
509 | server_hostname=hostname,
510 | ssl_context=ssl_context,
511 | )
512 |
513 | if ssl_context.verify_mode != ssl.CERT_NONE and not getattr(
514 | ssl_context, "check_hostname", False
515 | ):
516 | # While urllib3 attempts to always turn off hostname matching from
517 | # the TLS library, this cannot always be done. So we check whether
518 | # the TLS Library still thinks it's matching hostnames.
519 | cert = socket.getpeercert()
520 | if not cert.get("subjectAltName", ()):
521 | warnings.warn(
522 | (
523 | "Certificate for {0} has no `subjectAltName`, falling back to check for a "
524 | "`commonName` for now. This feature is being removed by major browsers and "
525 | "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 "
526 | "for details.)".format(hostname)
527 | ),
528 | SubjectAltNameWarning,
529 | )
530 | _match_hostname(cert, hostname)
531 |
532 | self.proxy_is_verified = ssl_context.verify_mode == ssl.CERT_REQUIRED
533 | return socket
534 |
535 |
536 | def _match_hostname(cert, asserted_hostname):
537 | # Our upstream implementation of ssl.match_hostname()
538 | # only applies this normalization to IP addresses so it doesn't
539 | # match DNS SANs so we do the same thing!
540 | stripped_hostname = asserted_hostname.strip("u[]")
541 | if is_ipaddress(stripped_hostname):
542 | asserted_hostname = stripped_hostname
543 |
544 | try:
545 | match_hostname(cert, asserted_hostname)
546 | except CertificateError as e:
547 | log.warning(
548 | "Certificate did not match expected hostname: %s. Certificate: %s",
549 | asserted_hostname,
550 | cert,
551 | )
552 | # Add cert to exception and reraise so client code can inspect
553 | # the cert when catching the exception, if they want to
554 | e._peer_cert = cert
555 | raise
556 |
557 |
558 | def _get_default_user_agent():
559 | return "python-urllib3/%s" % __version__
560 |
561 |
562 | class DummyConnection(object):
563 | """Used to detect a failed ConnectionCls import."""
564 |
565 | pass
566 |
567 |
568 | if not ssl:
569 | HTTPSConnection = DummyConnection # noqa: F811
570 |
571 |
572 | VerifiedHTTPSConnection = HTTPSConnection
573 |
```