This is page 93 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/lxml/html/diff.py:
--------------------------------------------------------------------------------
```python
1 | # cython: language_level=3
2 |
3 |
4 | import difflib
5 | from lxml import etree
6 | from lxml.html import fragment_fromstring
7 | import re
8 |
9 | __all__ = ['html_annotate', 'htmldiff']
10 |
11 | try:
12 | from html import escape as html_escape
13 | except ImportError:
14 | from cgi import escape as html_escape
15 | try:
16 | _unicode = unicode
17 | except NameError:
18 | # Python 3
19 | _unicode = str
20 | try:
21 | basestring
22 | except NameError:
23 | # Python 3
24 | basestring = str
25 |
26 | ############################################################
27 | ## Annotation
28 | ############################################################
29 |
30 | def default_markup(text, version):
31 | return '<span title="%s">%s</span>' % (
32 | html_escape(_unicode(version), 1), text)
33 |
34 | def html_annotate(doclist, markup=default_markup):
35 | """
36 | doclist should be ordered from oldest to newest, like::
37 |
38 | >>> version1 = 'Hello World'
39 | >>> version2 = 'Goodbye World'
40 | >>> print(html_annotate([(version1, 'version 1'),
41 | ... (version2, 'version 2')]))
42 | <span title="version 2">Goodbye</span> <span title="version 1">World</span>
43 |
44 | The documents must be *fragments* (str/UTF8 or unicode), not
45 | complete documents
46 |
47 | The markup argument is a function to markup the spans of words.
48 | This function is called like markup('Hello', 'version 2'), and
49 | returns HTML. The first argument is text and never includes any
50 | markup. The default uses a span with a title:
51 |
52 | >>> print(default_markup('Some Text', 'by Joe'))
53 | <span title="by Joe">Some Text</span>
54 | """
55 | # The basic strategy we have is to split the documents up into
56 | # logical tokens (which are words with attached markup). We then
57 | # do diffs of each of the versions to track when a token first
58 | # appeared in the document; the annotation attached to the token
59 | # is the version where it first appeared.
60 | tokenlist = [tokenize_annotated(doc, version)
61 | for doc, version in doclist]
62 | cur_tokens = tokenlist[0]
63 | for tokens in tokenlist[1:]:
64 | html_annotate_merge_annotations(cur_tokens, tokens)
65 | cur_tokens = tokens
66 |
67 | # After we've tracked all the tokens, we can combine spans of text
68 | # that are adjacent and have the same annotation
69 | cur_tokens = compress_tokens(cur_tokens)
70 | # And finally add markup
71 | result = markup_serialize_tokens(cur_tokens, markup)
72 | return ''.join(result).strip()
73 |
74 | def tokenize_annotated(doc, annotation):
75 | """Tokenize a document and add an annotation attribute to each token
76 | """
77 | tokens = tokenize(doc, include_hrefs=False)
78 | for tok in tokens:
79 | tok.annotation = annotation
80 | return tokens
81 |
82 | def html_annotate_merge_annotations(tokens_old, tokens_new):
83 | """Merge the annotations from tokens_old into tokens_new, when the
84 | tokens in the new document already existed in the old document.
85 | """
86 | s = InsensitiveSequenceMatcher(a=tokens_old, b=tokens_new)
87 | commands = s.get_opcodes()
88 |
89 | for command, i1, i2, j1, j2 in commands:
90 | if command == 'equal':
91 | eq_old = tokens_old[i1:i2]
92 | eq_new = tokens_new[j1:j2]
93 | copy_annotations(eq_old, eq_new)
94 |
95 | def copy_annotations(src, dest):
96 | """
97 | Copy annotations from the tokens listed in src to the tokens in dest
98 | """
99 | assert len(src) == len(dest)
100 | for src_tok, dest_tok in zip(src, dest):
101 | dest_tok.annotation = src_tok.annotation
102 |
103 | def compress_tokens(tokens):
104 | """
105 | Combine adjacent tokens when there is no HTML between the tokens,
106 | and they share an annotation
107 | """
108 | result = [tokens[0]]
109 | for tok in tokens[1:]:
110 | if (not result[-1].post_tags and
111 | not tok.pre_tags and
112 | result[-1].annotation == tok.annotation):
113 | compress_merge_back(result, tok)
114 | else:
115 | result.append(tok)
116 | return result
117 |
118 | def compress_merge_back(tokens, tok):
119 | """ Merge tok into the last element of tokens (modifying the list of
120 | tokens in-place). """
121 | last = tokens[-1]
122 | if type(last) is not token or type(tok) is not token:
123 | tokens.append(tok)
124 | else:
125 | text = _unicode(last)
126 | if last.trailing_whitespace:
127 | text += last.trailing_whitespace
128 | text += tok
129 | merged = token(text,
130 | pre_tags=last.pre_tags,
131 | post_tags=tok.post_tags,
132 | trailing_whitespace=tok.trailing_whitespace)
133 | merged.annotation = last.annotation
134 | tokens[-1] = merged
135 |
136 | def markup_serialize_tokens(tokens, markup_func):
137 | """
138 | Serialize the list of tokens into a list of text chunks, calling
139 | markup_func around text to add annotations.
140 | """
141 | for token in tokens:
142 | yield from token.pre_tags
143 | html = token.html()
144 | html = markup_func(html, token.annotation)
145 | if token.trailing_whitespace:
146 | html += token.trailing_whitespace
147 | yield html
148 | yield from token.post_tags
149 |
150 |
151 | ############################################################
152 | ## HTML Diffs
153 | ############################################################
154 |
155 | def htmldiff(old_html, new_html):
156 | ## FIXME: this should take parsed documents too, and use their body
157 | ## or other content.
158 | """ Do a diff of the old and new document. The documents are HTML
159 | *fragments* (str/UTF8 or unicode), they are not complete documents
160 | (i.e., no <html> tag).
161 |
162 | Returns HTML with <ins> and <del> tags added around the
163 | appropriate text.
164 |
165 | Markup is generally ignored, with the markup from new_html
166 | preserved, and possibly some markup from old_html (though it is
167 | considered acceptable to lose some of the old markup). Only the
168 | words in the HTML are diffed. The exception is <img> tags, which
169 | are treated like words, and the href attribute of <a> tags, which
170 | are noted inside the tag itself when there are changes.
171 | """
172 | old_html_tokens = tokenize(old_html)
173 | new_html_tokens = tokenize(new_html)
174 | result = htmldiff_tokens(old_html_tokens, new_html_tokens)
175 | result = ''.join(result).strip()
176 | return fixup_ins_del_tags(result)
177 |
178 | def htmldiff_tokens(html1_tokens, html2_tokens):
179 | """ Does a diff on the tokens themselves, returning a list of text
180 | chunks (not tokens).
181 | """
182 | # There are several passes as we do the differences. The tokens
183 | # isolate the portion of the content we care to diff; difflib does
184 | # all the actual hard work at that point.
185 | #
186 | # Then we must create a valid document from pieces of both the old
187 | # document and the new document. We generally prefer to take
188 | # markup from the new document, and only do a best effort attempt
189 | # to keep markup from the old document; anything that we can't
190 | # resolve we throw away. Also we try to put the deletes as close
191 | # to the location where we think they would have been -- because
192 | # we are only keeping the markup from the new document, it can be
193 | # fuzzy where in the new document the old text would have gone.
194 | # Again we just do a best effort attempt.
195 | s = InsensitiveSequenceMatcher(a=html1_tokens, b=html2_tokens)
196 | commands = s.get_opcodes()
197 | result = []
198 | for command, i1, i2, j1, j2 in commands:
199 | if command == 'equal':
200 | result.extend(expand_tokens(html2_tokens[j1:j2], equal=True))
201 | continue
202 | if command == 'insert' or command == 'replace':
203 | ins_tokens = expand_tokens(html2_tokens[j1:j2])
204 | merge_insert(ins_tokens, result)
205 | if command == 'delete' or command == 'replace':
206 | del_tokens = expand_tokens(html1_tokens[i1:i2])
207 | merge_delete(del_tokens, result)
208 | # If deletes were inserted directly as <del> then we'd have an
209 | # invalid document at this point. Instead we put in special
210 | # markers, and when the complete diffed document has been created
211 | # we try to move the deletes around and resolve any problems.
212 | result = cleanup_delete(result)
213 |
214 | return result
215 |
216 | def expand_tokens(tokens, equal=False):
217 | """Given a list of tokens, return a generator of the chunks of
218 | text for the data in the tokens.
219 | """
220 | for token in tokens:
221 | yield from token.pre_tags
222 | if not equal or not token.hide_when_equal:
223 | if token.trailing_whitespace:
224 | yield token.html() + token.trailing_whitespace
225 | else:
226 | yield token.html()
227 | yield from token.post_tags
228 |
229 | def merge_insert(ins_chunks, doc):
230 | """ doc is the already-handled document (as a list of text chunks);
231 | here we add <ins>ins_chunks</ins> to the end of that. """
232 | # Though we don't throw away unbalanced_start or unbalanced_end
233 | # (we assume there is accompanying markup later or earlier in the
234 | # document), we only put <ins> around the balanced portion.
235 | unbalanced_start, balanced, unbalanced_end = split_unbalanced(ins_chunks)
236 | doc.extend(unbalanced_start)
237 | if doc and not doc[-1].endswith(' '):
238 | # Fix up the case where the word before the insert didn't end with
239 | # a space
240 | doc[-1] += ' '
241 | doc.append('<ins>')
242 | if balanced and balanced[-1].endswith(' '):
243 | # We move space outside of </ins>
244 | balanced[-1] = balanced[-1][:-1]
245 | doc.extend(balanced)
246 | doc.append('</ins> ')
247 | doc.extend(unbalanced_end)
248 |
249 | # These are sentinels to represent the start and end of a <del>
250 | # segment, until we do the cleanup phase to turn them into proper
251 | # markup:
252 | class DEL_START:
253 | pass
254 | class DEL_END:
255 | pass
256 |
257 | class NoDeletes(Exception):
258 | """ Raised when the document no longer contains any pending deletes
259 | (DEL_START/DEL_END) """
260 |
261 | def merge_delete(del_chunks, doc):
262 | """ Adds the text chunks in del_chunks to the document doc (another
263 | list of text chunks) with marker to show it is a delete.
264 | cleanup_delete later resolves these markers into <del> tags."""
265 | doc.append(DEL_START)
266 | doc.extend(del_chunks)
267 | doc.append(DEL_END)
268 |
269 | def cleanup_delete(chunks):
270 | """ Cleans up any DEL_START/DEL_END markers in the document, replacing
271 | them with <del></del>. To do this while keeping the document
272 | valid, it may need to drop some tags (either start or end tags).
273 |
274 | It may also move the del into adjacent tags to try to move it to a
275 | similar location where it was originally located (e.g., moving a
276 | delete into preceding <div> tag, if the del looks like (DEL_START,
277 | 'Text</div>', DEL_END)"""
278 | while 1:
279 | # Find a pending DEL_START/DEL_END, splitting the document
280 | # into stuff-preceding-DEL_START, stuff-inside, and
281 | # stuff-following-DEL_END
282 | try:
283 | pre_delete, delete, post_delete = split_delete(chunks)
284 | except NoDeletes:
285 | # Nothing found, we've cleaned up the entire doc
286 | break
287 | # The stuff-inside-DEL_START/END may not be well balanced
288 | # markup. First we figure out what unbalanced portions there are:
289 | unbalanced_start, balanced, unbalanced_end = split_unbalanced(delete)
290 | # Then we move the span forward and/or backward based on these
291 | # unbalanced portions:
292 | locate_unbalanced_start(unbalanced_start, pre_delete, post_delete)
293 | locate_unbalanced_end(unbalanced_end, pre_delete, post_delete)
294 | doc = pre_delete
295 | if doc and not doc[-1].endswith(' '):
296 | # Fix up case where the word before us didn't have a trailing space
297 | doc[-1] += ' '
298 | doc.append('<del>')
299 | if balanced and balanced[-1].endswith(' '):
300 | # We move space outside of </del>
301 | balanced[-1] = balanced[-1][:-1]
302 | doc.extend(balanced)
303 | doc.append('</del> ')
304 | doc.extend(post_delete)
305 | chunks = doc
306 | return chunks
307 |
308 | def split_unbalanced(chunks):
309 | """Return (unbalanced_start, balanced, unbalanced_end), where each is
310 | a list of text and tag chunks.
311 |
312 | unbalanced_start is a list of all the tags that are opened, but
313 | not closed in this span. Similarly, unbalanced_end is a list of
314 | tags that are closed but were not opened. Extracting these might
315 | mean some reordering of the chunks."""
316 | start = []
317 | end = []
318 | tag_stack = []
319 | balanced = []
320 | for chunk in chunks:
321 | if not chunk.startswith('<'):
322 | balanced.append(chunk)
323 | continue
324 | endtag = chunk[1] == '/'
325 | name = chunk.split()[0].strip('<>/')
326 | if name in empty_tags:
327 | balanced.append(chunk)
328 | continue
329 | if endtag:
330 | if tag_stack and tag_stack[-1][0] == name:
331 | balanced.append(chunk)
332 | name, pos, tag = tag_stack.pop()
333 | balanced[pos] = tag
334 | elif tag_stack:
335 | start.extend([tag for name, pos, tag in tag_stack])
336 | tag_stack = []
337 | end.append(chunk)
338 | else:
339 | end.append(chunk)
340 | else:
341 | tag_stack.append((name, len(balanced), chunk))
342 | balanced.append(None)
343 | start.extend(
344 | [chunk for name, pos, chunk in tag_stack])
345 | balanced = [chunk for chunk in balanced if chunk is not None]
346 | return start, balanced, end
347 |
348 | def split_delete(chunks):
349 | """ Returns (stuff_before_DEL_START, stuff_inside_DEL_START_END,
350 | stuff_after_DEL_END). Returns the first case found (there may be
351 | more DEL_STARTs in stuff_after_DEL_END). Raises NoDeletes if
352 | there's no DEL_START found. """
353 | try:
354 | pos = chunks.index(DEL_START)
355 | except ValueError:
356 | raise NoDeletes
357 | pos2 = chunks.index(DEL_END)
358 | return chunks[:pos], chunks[pos+1:pos2], chunks[pos2+1:]
359 |
360 | def locate_unbalanced_start(unbalanced_start, pre_delete, post_delete):
361 | """ pre_delete and post_delete implicitly point to a place in the
362 | document (where the two were split). This moves that point (by
363 | popping items from one and pushing them onto the other). It moves
364 | the point to try to find a place where unbalanced_start applies.
365 |
366 | As an example::
367 |
368 | >>> unbalanced_start = ['<div>']
369 | >>> doc = ['<p>', 'Text', '</p>', '<div>', 'More Text', '</div>']
370 | >>> pre, post = doc[:3], doc[3:]
371 | >>> pre, post
372 | (['<p>', 'Text', '</p>'], ['<div>', 'More Text', '</div>'])
373 | >>> locate_unbalanced_start(unbalanced_start, pre, post)
374 | >>> pre, post
375 | (['<p>', 'Text', '</p>', '<div>'], ['More Text', '</div>'])
376 |
377 | As you can see, we moved the point so that the dangling <div> that
378 | we found will be effectively replaced by the div in the original
379 | document. If this doesn't work out, we just throw away
380 | unbalanced_start without doing anything.
381 | """
382 | while 1:
383 | if not unbalanced_start:
384 | # We have totally succeeded in finding the position
385 | break
386 | finding = unbalanced_start[0]
387 | finding_name = finding.split()[0].strip('<>')
388 | if not post_delete:
389 | break
390 | next = post_delete[0]
391 | if next is DEL_START or not next.startswith('<'):
392 | # Reached a word, we can't move the delete text forward
393 | break
394 | if next[1] == '/':
395 | # Reached a closing tag, can we go further? Maybe not...
396 | break
397 | name = next.split()[0].strip('<>')
398 | if name == 'ins':
399 | # Can't move into an insert
400 | break
401 | assert name != 'del', (
402 | "Unexpected delete tag: %r" % next)
403 | if name == finding_name:
404 | unbalanced_start.pop(0)
405 | pre_delete.append(post_delete.pop(0))
406 | else:
407 | # Found a tag that doesn't match
408 | break
409 |
410 | def locate_unbalanced_end(unbalanced_end, pre_delete, post_delete):
411 | """ like locate_unbalanced_start, except handling end tags and
412 | possibly moving the point earlier in the document. """
413 | while 1:
414 | if not unbalanced_end:
415 | # Success
416 | break
417 | finding = unbalanced_end[-1]
418 | finding_name = finding.split()[0].strip('<>/')
419 | if not pre_delete:
420 | break
421 | next = pre_delete[-1]
422 | if next is DEL_END or not next.startswith('</'):
423 | # A word or a start tag
424 | break
425 | name = next.split()[0].strip('<>/')
426 | if name == 'ins' or name == 'del':
427 | # Can't move into an insert or delete
428 | break
429 | if name == finding_name:
430 | unbalanced_end.pop()
431 | post_delete.insert(0, pre_delete.pop())
432 | else:
433 | # Found a tag that doesn't match
434 | break
435 |
436 | class token(_unicode):
437 | """ Represents a diffable token, generally a word that is displayed to
438 | the user. Opening tags are attached to this token when they are
439 | adjacent (pre_tags) and closing tags that follow the word
440 | (post_tags). Some exceptions occur when there are empty tags
441 | adjacent to a word, so there may be close tags in pre_tags, or
442 | open tags in post_tags.
443 |
444 | We also keep track of whether the word was originally followed by
445 | whitespace, even though we do not want to treat the word as
446 | equivalent to a similar word that does not have a trailing
447 | space."""
448 |
449 | # When this is true, the token will be eliminated from the
450 | # displayed diff if no change has occurred:
451 | hide_when_equal = False
452 |
453 | def __new__(cls, text, pre_tags=None, post_tags=None, trailing_whitespace=""):
454 | obj = _unicode.__new__(cls, text)
455 |
456 | if pre_tags is not None:
457 | obj.pre_tags = pre_tags
458 | else:
459 | obj.pre_tags = []
460 |
461 | if post_tags is not None:
462 | obj.post_tags = post_tags
463 | else:
464 | obj.post_tags = []
465 |
466 | obj.trailing_whitespace = trailing_whitespace
467 |
468 | return obj
469 |
470 | def __repr__(self):
471 | return 'token(%s, %r, %r, %r)' % (_unicode.__repr__(self), self.pre_tags,
472 | self.post_tags, self.trailing_whitespace)
473 |
474 | def html(self):
475 | return _unicode(self)
476 |
477 | class tag_token(token):
478 |
479 | """ Represents a token that is actually a tag. Currently this is just
480 | the <img> tag, which takes up visible space just like a word but
481 | is only represented in a document by a tag. """
482 |
483 | def __new__(cls, tag, data, html_repr, pre_tags=None,
484 | post_tags=None, trailing_whitespace=""):
485 | obj = token.__new__(cls, "%s: %s" % (type, data),
486 | pre_tags=pre_tags,
487 | post_tags=post_tags,
488 | trailing_whitespace=trailing_whitespace)
489 | obj.tag = tag
490 | obj.data = data
491 | obj.html_repr = html_repr
492 | return obj
493 |
494 | def __repr__(self):
495 | return 'tag_token(%s, %s, html_repr=%s, post_tags=%r, pre_tags=%r, trailing_whitespace=%r)' % (
496 | self.tag,
497 | self.data,
498 | self.html_repr,
499 | self.pre_tags,
500 | self.post_tags,
501 | self.trailing_whitespace)
502 | def html(self):
503 | return self.html_repr
504 |
505 | class href_token(token):
506 |
507 | """ Represents the href in an anchor tag. Unlike other words, we only
508 | show the href when it changes. """
509 |
510 | hide_when_equal = True
511 |
512 | def html(self):
513 | return ' Link: %s' % self
514 |
515 | def tokenize(html, include_hrefs=True):
516 | """
517 | Parse the given HTML and returns token objects (words with attached tags).
518 |
519 | This parses only the content of a page; anything in the head is
520 | ignored, and the <head> and <body> elements are themselves
521 | optional. The content is then parsed by lxml, which ensures the
522 | validity of the resulting parsed document (though lxml may make
523 | incorrect guesses when the markup is particular bad).
524 |
525 | <ins> and <del> tags are also eliminated from the document, as
526 | that gets confusing.
527 |
528 | If include_hrefs is true, then the href attribute of <a> tags is
529 | included as a special kind of diffable token."""
530 | if etree.iselement(html):
531 | body_el = html
532 | else:
533 | body_el = parse_html(html, cleanup=True)
534 | # Then we split the document into text chunks for each tag, word, and end tag:
535 | chunks = flatten_el(body_el, skip_tag=True, include_hrefs=include_hrefs)
536 | # Finally re-joining them into token objects:
537 | return fixup_chunks(chunks)
538 |
539 | def parse_html(html, cleanup=True):
540 | """
541 | Parses an HTML fragment, returning an lxml element. Note that the HTML will be
542 | wrapped in a <div> tag that was not in the original document.
543 |
544 | If cleanup is true, make sure there's no <head> or <body>, and get
545 | rid of any <ins> and <del> tags.
546 | """
547 | if cleanup:
548 | # This removes any extra markup or structure like <head>:
549 | html = cleanup_html(html)
550 | return fragment_fromstring(html, create_parent=True)
551 |
552 | _body_re = re.compile(r'<body.*?>', re.I|re.S)
553 | _end_body_re = re.compile(r'</body.*?>', re.I|re.S)
554 | _ins_del_re = re.compile(r'</?(ins|del).*?>', re.I|re.S)
555 |
556 | def cleanup_html(html):
557 | """ This 'cleans' the HTML, meaning that any page structure is removed
558 | (only the contents of <body> are used, if there is any <body).
559 | Also <ins> and <del> tags are removed. """
560 | match = _body_re.search(html)
561 | if match:
562 | html = html[match.end():]
563 | match = _end_body_re.search(html)
564 | if match:
565 | html = html[:match.start()]
566 | html = _ins_del_re.sub('', html)
567 | return html
568 |
569 |
570 | end_whitespace_re = re.compile(r'[ \t\n\r]$')
571 |
572 | def split_trailing_whitespace(word):
573 | """
574 | This function takes a word, such as 'test\n\n' and returns ('test','\n\n')
575 | """
576 | stripped_length = len(word.rstrip())
577 | return word[0:stripped_length], word[stripped_length:]
578 |
579 |
580 | def fixup_chunks(chunks):
581 | """
582 | This function takes a list of chunks and produces a list of tokens.
583 | """
584 | tag_accum = []
585 | cur_word = None
586 | result = []
587 | for chunk in chunks:
588 | if isinstance(chunk, tuple):
589 | if chunk[0] == 'img':
590 | src = chunk[1]
591 | tag, trailing_whitespace = split_trailing_whitespace(chunk[2])
592 | cur_word = tag_token('img', src, html_repr=tag,
593 | pre_tags=tag_accum,
594 | trailing_whitespace=trailing_whitespace)
595 | tag_accum = []
596 | result.append(cur_word)
597 |
598 | elif chunk[0] == 'href':
599 | href = chunk[1]
600 | cur_word = href_token(href, pre_tags=tag_accum, trailing_whitespace=" ")
601 | tag_accum = []
602 | result.append(cur_word)
603 | continue
604 |
605 | if is_word(chunk):
606 | chunk, trailing_whitespace = split_trailing_whitespace(chunk)
607 | cur_word = token(chunk, pre_tags=tag_accum, trailing_whitespace=trailing_whitespace)
608 | tag_accum = []
609 | result.append(cur_word)
610 |
611 | elif is_start_tag(chunk):
612 | tag_accum.append(chunk)
613 |
614 | elif is_end_tag(chunk):
615 | if tag_accum:
616 | tag_accum.append(chunk)
617 | else:
618 | assert cur_word, (
619 | "Weird state, cur_word=%r, result=%r, chunks=%r of %r"
620 | % (cur_word, result, chunk, chunks))
621 | cur_word.post_tags.append(chunk)
622 | else:
623 | assert False
624 |
625 | if not result:
626 | return [token('', pre_tags=tag_accum)]
627 | else:
628 | result[-1].post_tags.extend(tag_accum)
629 |
630 | return result
631 |
632 |
633 | # All the tags in HTML that don't require end tags:
634 | empty_tags = (
635 | 'param', 'img', 'area', 'br', 'basefont', 'input',
636 | 'base', 'meta', 'link', 'col')
637 |
638 | block_level_tags = (
639 | 'address',
640 | 'blockquote',
641 | 'center',
642 | 'dir',
643 | 'div',
644 | 'dl',
645 | 'fieldset',
646 | 'form',
647 | 'h1',
648 | 'h2',
649 | 'h3',
650 | 'h4',
651 | 'h5',
652 | 'h6',
653 | 'hr',
654 | 'isindex',
655 | 'menu',
656 | 'noframes',
657 | 'noscript',
658 | 'ol',
659 | 'p',
660 | 'pre',
661 | 'table',
662 | 'ul',
663 | )
664 |
665 | block_level_container_tags = (
666 | 'dd',
667 | 'dt',
668 | 'frameset',
669 | 'li',
670 | 'tbody',
671 | 'td',
672 | 'tfoot',
673 | 'th',
674 | 'thead',
675 | 'tr',
676 | )
677 |
678 |
679 | def flatten_el(el, include_hrefs, skip_tag=False):
680 | """ Takes an lxml element el, and generates all the text chunks for
681 | that tag. Each start tag is a chunk, each word is a chunk, and each
682 | end tag is a chunk.
683 |
684 | If skip_tag is true, then the outermost container tag is
685 | not returned (just its contents)."""
686 | if not skip_tag:
687 | if el.tag == 'img':
688 | yield ('img', el.get('src'), start_tag(el))
689 | else:
690 | yield start_tag(el)
691 | if el.tag in empty_tags and not el.text and not len(el) and not el.tail:
692 | return
693 | start_words = split_words(el.text)
694 | for word in start_words:
695 | yield html_escape(word)
696 | for child in el:
697 | yield from flatten_el(child, include_hrefs=include_hrefs)
698 | if el.tag == 'a' and el.get('href') and include_hrefs:
699 | yield ('href', el.get('href'))
700 | if not skip_tag:
701 | yield end_tag(el)
702 | end_words = split_words(el.tail)
703 | for word in end_words:
704 | yield html_escape(word)
705 |
706 | split_words_re = re.compile(r'\S+(?:\s+|$)', re.U)
707 |
708 | def split_words(text):
709 | """ Splits some text into words. Includes trailing whitespace
710 | on each word when appropriate. """
711 | if not text or not text.strip():
712 | return []
713 |
714 | words = split_words_re.findall(text)
715 | return words
716 |
717 | start_whitespace_re = re.compile(r'^[ \t\n\r]')
718 |
719 | def start_tag(el):
720 | """
721 | The text representation of the start tag for a tag.
722 | """
723 | return '<%s%s>' % (
724 | el.tag, ''.join([' %s="%s"' % (name, html_escape(value, True))
725 | for name, value in el.attrib.items()]))
726 |
727 | def end_tag(el):
728 | """ The text representation of an end tag for a tag. Includes
729 | trailing whitespace when appropriate. """
730 | if el.tail and start_whitespace_re.search(el.tail):
731 | extra = ' '
732 | else:
733 | extra = ''
734 | return '</%s>%s' % (el.tag, extra)
735 |
736 | def is_word(tok):
737 | return not tok.startswith('<')
738 |
739 | def is_end_tag(tok):
740 | return tok.startswith('</')
741 |
742 | def is_start_tag(tok):
743 | return tok.startswith('<') and not tok.startswith('</')
744 |
745 | def fixup_ins_del_tags(html):
746 | """ Given an html string, move any <ins> or <del> tags inside of any
747 | block-level elements, e.g. transform <ins><p>word</p></ins> to
748 | <p><ins>word</ins></p> """
749 | doc = parse_html(html, cleanup=False)
750 | _fixup_ins_del_tags(doc)
751 | html = serialize_html_fragment(doc, skip_outer=True)
752 | return html
753 |
754 | def serialize_html_fragment(el, skip_outer=False):
755 | """ Serialize a single lxml element as HTML. The serialized form
756 | includes the elements tail.
757 |
758 | If skip_outer is true, then don't serialize the outermost tag
759 | """
760 | assert not isinstance(el, basestring), (
761 | "You should pass in an element, not a string like %r" % el)
762 | html = etree.tostring(el, method="html", encoding=_unicode)
763 | if skip_outer:
764 | # Get rid of the extra starting tag:
765 | html = html[html.find('>')+1:]
766 | # Get rid of the extra end tag:
767 | html = html[:html.rfind('<')]
768 | return html.strip()
769 | else:
770 | return html
771 |
772 | def _fixup_ins_del_tags(doc):
773 | """fixup_ins_del_tags that works on an lxml document in-place
774 | """
775 | for tag in ['ins', 'del']:
776 | for el in doc.xpath('descendant-or-self::%s' % tag):
777 | if not _contains_block_level_tag(el):
778 | continue
779 | _move_el_inside_block(el, tag=tag)
780 | el.drop_tag()
781 | #_merge_element_contents(el)
782 |
783 | def _contains_block_level_tag(el):
784 | """True if the element contains any block-level elements, like <p>, <td>, etc.
785 | """
786 | if el.tag in block_level_tags or el.tag in block_level_container_tags:
787 | return True
788 | for child in el:
789 | if _contains_block_level_tag(child):
790 | return True
791 | return False
792 |
793 | def _move_el_inside_block(el, tag):
794 | """ helper for _fixup_ins_del_tags; actually takes the <ins> etc tags
795 | and moves them inside any block-level tags. """
796 | for child in el:
797 | if _contains_block_level_tag(child):
798 | break
799 | else:
800 | # No block-level tags in any child
801 | children_tag = etree.Element(tag)
802 | children_tag.text = el.text
803 | el.text = None
804 | children_tag.extend(list(el))
805 | el[:] = [children_tag]
806 | return
807 | for child in list(el):
808 | if _contains_block_level_tag(child):
809 | _move_el_inside_block(child, tag)
810 | if child.tail:
811 | tail_tag = etree.Element(tag)
812 | tail_tag.text = child.tail
813 | child.tail = None
814 | el.insert(el.index(child)+1, tail_tag)
815 | else:
816 | child_tag = etree.Element(tag)
817 | el.replace(child, child_tag)
818 | child_tag.append(child)
819 | if el.text:
820 | text_tag = etree.Element(tag)
821 | text_tag.text = el.text
822 | el.text = None
823 | el.insert(0, text_tag)
824 |
825 | def _merge_element_contents(el):
826 | """
827 | Removes an element, but merges its contents into its place, e.g.,
828 | given <p>Hi <i>there!</i></p>, if you remove the <i> element you get
829 | <p>Hi there!</p>
830 | """
831 | parent = el.getparent()
832 | text = el.text or ''
833 | if el.tail:
834 | if not len(el):
835 | text += el.tail
836 | else:
837 | if el[-1].tail:
838 | el[-1].tail += el.tail
839 | else:
840 | el[-1].tail = el.tail
841 | index = parent.index(el)
842 | if text:
843 | if index == 0:
844 | previous = None
845 | else:
846 | previous = parent[index-1]
847 | if previous is None:
848 | if parent.text:
849 | parent.text += text
850 | else:
851 | parent.text = text
852 | else:
853 | if previous.tail:
854 | previous.tail += text
855 | else:
856 | previous.tail = text
857 | parent[index:index+1] = el.getchildren()
858 |
859 | class InsensitiveSequenceMatcher(difflib.SequenceMatcher):
860 | """
861 | Acts like SequenceMatcher, but tries not to find very small equal
862 | blocks amidst large spans of changes
863 | """
864 |
865 | threshold = 2
866 |
867 | def get_matching_blocks(self):
868 | size = min(len(self.b), len(self.b))
869 | threshold = min(self.threshold, size / 4)
870 | actual = difflib.SequenceMatcher.get_matching_blocks(self)
871 | return [item for item in actual
872 | if item[2] > threshold
873 | or not item[2]]
874 |
875 | if __name__ == '__main__':
876 | from lxml.html import _diffcommand
877 | _diffcommand.main()
878 |
879 |
```
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | requests.sessions
3 | ~~~~~~~~~~~~~~~~~
4 |
5 | This module provides a Session object to manage and persist settings across
6 | requests (cookies, auth, proxies).
7 | """
8 | import os
9 | import sys
10 | import time
11 | from collections import OrderedDict
12 | from datetime import timedelta
13 |
14 | from ._internal_utils import to_native_string
15 | from .adapters import HTTPAdapter
16 | from .auth import _basic_auth_str
17 | from .compat import Mapping, cookielib, urljoin, urlparse
18 | from .cookies import (
19 | RequestsCookieJar,
20 | cookiejar_from_dict,
21 | extract_cookies_to_jar,
22 | merge_cookies,
23 | )
24 | from .exceptions import (
25 | ChunkedEncodingError,
26 | ContentDecodingError,
27 | InvalidSchema,
28 | TooManyRedirects,
29 | )
30 | from .hooks import default_hooks, dispatch_hook
31 |
32 | # formerly defined here, reexposed here for backward compatibility
33 | from .models import ( # noqa: F401
34 | DEFAULT_REDIRECT_LIMIT,
35 | REDIRECT_STATI,
36 | PreparedRequest,
37 | Request,
38 | )
39 | from .status_codes import codes
40 | from .structures import CaseInsensitiveDict
41 | from .utils import ( # noqa: F401
42 | DEFAULT_PORTS,
43 | default_headers,
44 | get_auth_from_url,
45 | get_environ_proxies,
46 | get_netrc_auth,
47 | requote_uri,
48 | resolve_proxies,
49 | rewind_body,
50 | should_bypass_proxies,
51 | to_key_val_list,
52 | )
53 |
54 | # Preferred clock, based on which one is more accurate on a given system.
55 | if sys.platform == "win32":
56 | preferred_clock = time.perf_counter
57 | else:
58 | preferred_clock = time.time
59 |
60 |
61 | def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
62 | """Determines appropriate setting for a given request, taking into account
63 | the explicit setting on that request, and the setting in the session. If a
64 | setting is a dictionary, they will be merged together using `dict_class`
65 | """
66 |
67 | if session_setting is None:
68 | return request_setting
69 |
70 | if request_setting is None:
71 | return session_setting
72 |
73 | # Bypass if not a dictionary (e.g. verify)
74 | if not (
75 | isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)
76 | ):
77 | return request_setting
78 |
79 | merged_setting = dict_class(to_key_val_list(session_setting))
80 | merged_setting.update(to_key_val_list(request_setting))
81 |
82 | # Remove keys that are set to None. Extract keys first to avoid altering
83 | # the dictionary during iteration.
84 | none_keys = [k for (k, v) in merged_setting.items() if v is None]
85 | for key in none_keys:
86 | del merged_setting[key]
87 |
88 | return merged_setting
89 |
90 |
91 | def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
92 | """Properly merges both requests and session hooks.
93 |
94 | This is necessary because when request_hooks == {'response': []}, the
95 | merge breaks Session hooks entirely.
96 | """
97 | if session_hooks is None or session_hooks.get("response") == []:
98 | return request_hooks
99 |
100 | if request_hooks is None or request_hooks.get("response") == []:
101 | return session_hooks
102 |
103 | return merge_setting(request_hooks, session_hooks, dict_class)
104 |
105 |
106 | class SessionRedirectMixin:
107 | def get_redirect_target(self, resp):
108 | """Receives a Response. Returns a redirect URI or ``None``"""
109 | # Due to the nature of how requests processes redirects this method will
110 | # be called at least once upon the original response and at least twice
111 | # on each subsequent redirect response (if any).
112 | # If a custom mixin is used to handle this logic, it may be advantageous
113 | # to cache the redirect location onto the response object as a private
114 | # attribute.
115 | if resp.is_redirect:
116 | location = resp.headers["location"]
117 | # Currently the underlying http module on py3 decode headers
118 | # in latin1, but empirical evidence suggests that latin1 is very
119 | # rarely used with non-ASCII characters in HTTP headers.
120 | # It is more likely to get UTF8 header rather than latin1.
121 | # This causes incorrect handling of UTF8 encoded location headers.
122 | # To solve this, we re-encode the location in latin1.
123 | location = location.encode("latin1")
124 | return to_native_string(location, "utf8")
125 | return None
126 |
127 | def should_strip_auth(self, old_url, new_url):
128 | """Decide whether Authorization header should be removed when redirecting"""
129 | old_parsed = urlparse(old_url)
130 | new_parsed = urlparse(new_url)
131 | if old_parsed.hostname != new_parsed.hostname:
132 | return True
133 | # Special case: allow http -> https redirect when using the standard
134 | # ports. This isn't specified by RFC 7235, but is kept to avoid
135 | # breaking backwards compatibility with older versions of requests
136 | # that allowed any redirects on the same host.
137 | if (
138 | old_parsed.scheme == "http"
139 | and old_parsed.port in (80, None)
140 | and new_parsed.scheme == "https"
141 | and new_parsed.port in (443, None)
142 | ):
143 | return False
144 |
145 | # Handle default port usage corresponding to scheme.
146 | changed_port = old_parsed.port != new_parsed.port
147 | changed_scheme = old_parsed.scheme != new_parsed.scheme
148 | default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
149 | if (
150 | not changed_scheme
151 | and old_parsed.port in default_port
152 | and new_parsed.port in default_port
153 | ):
154 | return False
155 |
156 | # Standard case: root URI must match
157 | return changed_port or changed_scheme
158 |
159 | def resolve_redirects(
160 | self,
161 | resp,
162 | req,
163 | stream=False,
164 | timeout=None,
165 | verify=True,
166 | cert=None,
167 | proxies=None,
168 | yield_requests=False,
169 | **adapter_kwargs,
170 | ):
171 | """Receives a Response. Returns a generator of Responses or Requests."""
172 |
173 | hist = [] # keep track of history
174 |
175 | url = self.get_redirect_target(resp)
176 | previous_fragment = urlparse(req.url).fragment
177 | while url:
178 | prepared_request = req.copy()
179 |
180 | # Update history and keep track of redirects.
181 | # resp.history must ignore the original request in this loop
182 | hist.append(resp)
183 | resp.history = hist[1:]
184 |
185 | try:
186 | resp.content # Consume socket so it can be released
187 | except (ChunkedEncodingError, ContentDecodingError, RuntimeError):
188 | resp.raw.read(decode_content=False)
189 |
190 | if len(resp.history) >= self.max_redirects:
191 | raise TooManyRedirects(
192 | f"Exceeded {self.max_redirects} redirects.", response=resp
193 | )
194 |
195 | # Release the connection back into the pool.
196 | resp.close()
197 |
198 | # Handle redirection without scheme (see: RFC 1808 Section 4)
199 | if url.startswith("//"):
200 | parsed_rurl = urlparse(resp.url)
201 | url = ":".join([to_native_string(parsed_rurl.scheme), url])
202 |
203 | # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
204 | parsed = urlparse(url)
205 | if parsed.fragment == "" and previous_fragment:
206 | parsed = parsed._replace(fragment=previous_fragment)
207 | elif parsed.fragment:
208 | previous_fragment = parsed.fragment
209 | url = parsed.geturl()
210 |
211 | # Facilitate relative 'location' headers, as allowed by RFC 7231.
212 | # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
213 | # Compliant with RFC3986, we percent encode the url.
214 | if not parsed.netloc:
215 | url = urljoin(resp.url, requote_uri(url))
216 | else:
217 | url = requote_uri(url)
218 |
219 | prepared_request.url = to_native_string(url)
220 |
221 | self.rebuild_method(prepared_request, resp)
222 |
223 | # https://github.com/psf/requests/issues/1084
224 | if resp.status_code not in (
225 | codes.temporary_redirect,
226 | codes.permanent_redirect,
227 | ):
228 | # https://github.com/psf/requests/issues/3490
229 | purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")
230 | for header in purged_headers:
231 | prepared_request.headers.pop(header, None)
232 | prepared_request.body = None
233 |
234 | headers = prepared_request.headers
235 | headers.pop("Cookie", None)
236 |
237 | # Extract any cookies sent on the response to the cookiejar
238 | # in the new request. Because we've mutated our copied prepared
239 | # request, use the old one that we haven't yet touched.
240 | extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
241 | merge_cookies(prepared_request._cookies, self.cookies)
242 | prepared_request.prepare_cookies(prepared_request._cookies)
243 |
244 | # Rebuild auth and proxy information.
245 | proxies = self.rebuild_proxies(prepared_request, proxies)
246 | self.rebuild_auth(prepared_request, resp)
247 |
248 | # A failed tell() sets `_body_position` to `object()`. This non-None
249 | # value ensures `rewindable` will be True, allowing us to raise an
250 | # UnrewindableBodyError, instead of hanging the connection.
251 | rewindable = prepared_request._body_position is not None and (
252 | "Content-Length" in headers or "Transfer-Encoding" in headers
253 | )
254 |
255 | # Attempt to rewind consumed file-like object.
256 | if rewindable:
257 | rewind_body(prepared_request)
258 |
259 | # Override the original request.
260 | req = prepared_request
261 |
262 | if yield_requests:
263 | yield req
264 | else:
265 | resp = self.send(
266 | req,
267 | stream=stream,
268 | timeout=timeout,
269 | verify=verify,
270 | cert=cert,
271 | proxies=proxies,
272 | allow_redirects=False,
273 | **adapter_kwargs,
274 | )
275 |
276 | extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
277 |
278 | # extract redirect url, if any, for the next loop
279 | url = self.get_redirect_target(resp)
280 | yield resp
281 |
282 | def rebuild_auth(self, prepared_request, response):
283 | """When being redirected we may want to strip authentication from the
284 | request to avoid leaking credentials. This method intelligently removes
285 | and reapplies authentication where possible to avoid credential loss.
286 | """
287 | headers = prepared_request.headers
288 | url = prepared_request.url
289 |
290 | if "Authorization" in headers and self.should_strip_auth(
291 | response.request.url, url
292 | ):
293 | # If we get redirected to a new host, we should strip out any
294 | # authentication headers.
295 | del headers["Authorization"]
296 |
297 | # .netrc might have more auth for us on our new host.
298 | new_auth = get_netrc_auth(url) if self.trust_env else None
299 | if new_auth is not None:
300 | prepared_request.prepare_auth(new_auth)
301 |
302 | def rebuild_proxies(self, prepared_request, proxies):
303 | """This method re-evaluates the proxy configuration by considering the
304 | environment variables. If we are redirected to a URL covered by
305 | NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
306 | proxy keys for this URL (in case they were stripped by a previous
307 | redirect).
308 |
309 | This method also replaces the Proxy-Authorization header where
310 | necessary.
311 |
312 | :rtype: dict
313 | """
314 | headers = prepared_request.headers
315 | scheme = urlparse(prepared_request.url).scheme
316 | new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
317 |
318 | if "Proxy-Authorization" in headers:
319 | del headers["Proxy-Authorization"]
320 |
321 | try:
322 | username, password = get_auth_from_url(new_proxies[scheme])
323 | except KeyError:
324 | username, password = None, None
325 |
326 | # urllib3 handles proxy authorization for us in the standard adapter.
327 | # Avoid appending this to TLS tunneled requests where it may be leaked.
328 | if not scheme.startswith("https") and username and password:
329 | headers["Proxy-Authorization"] = _basic_auth_str(username, password)
330 |
331 | return new_proxies
332 |
333 | def rebuild_method(self, prepared_request, response):
334 | """When being redirected we may want to change the method of the request
335 | based on certain specs or browser behavior.
336 | """
337 | method = prepared_request.method
338 |
339 | # https://tools.ietf.org/html/rfc7231#section-6.4.4
340 | if response.status_code == codes.see_other and method != "HEAD":
341 | method = "GET"
342 |
343 | # Do what the browsers do, despite standards...
344 | # First, turn 302s into GETs.
345 | if response.status_code == codes.found and method != "HEAD":
346 | method = "GET"
347 |
348 | # Second, if a POST is responded to with a 301, turn it into a GET.
349 | # This bizarre behaviour is explained in Issue 1704.
350 | if response.status_code == codes.moved and method == "POST":
351 | method = "GET"
352 |
353 | prepared_request.method = method
354 |
355 |
356 | class Session(SessionRedirectMixin):
357 | """A Requests session.
358 |
359 | Provides cookie persistence, connection-pooling, and configuration.
360 |
361 | Basic Usage::
362 |
363 | >>> import requests
364 | >>> s = requests.Session()
365 | >>> s.get('https://httpbin.org/get')
366 | <Response [200]>
367 |
368 | Or as a context manager::
369 |
370 | >>> with requests.Session() as s:
371 | ... s.get('https://httpbin.org/get')
372 | <Response [200]>
373 | """
374 |
375 | __attrs__ = [
376 | "headers",
377 | "cookies",
378 | "auth",
379 | "proxies",
380 | "hooks",
381 | "params",
382 | "verify",
383 | "cert",
384 | "adapters",
385 | "stream",
386 | "trust_env",
387 | "max_redirects",
388 | ]
389 |
390 | def __init__(self):
391 | #: A case-insensitive dictionary of headers to be sent on each
392 | #: :class:`Request <Request>` sent from this
393 | #: :class:`Session <Session>`.
394 | self.headers = default_headers()
395 |
396 | #: Default Authentication tuple or object to attach to
397 | #: :class:`Request <Request>`.
398 | self.auth = None
399 |
400 | #: Dictionary mapping protocol or protocol and host to the URL of the proxy
401 | #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
402 | #: be used on each :class:`Request <Request>`.
403 | self.proxies = {}
404 |
405 | #: Event-handling hooks.
406 | self.hooks = default_hooks()
407 |
408 | #: Dictionary of querystring data to attach to each
409 | #: :class:`Request <Request>`. The dictionary values may be lists for
410 | #: representing multivalued query parameters.
411 | self.params = {}
412 |
413 | #: Stream response content default.
414 | self.stream = False
415 |
416 | #: SSL Verification default.
417 | #: Defaults to `True`, requiring requests to verify the TLS certificate at the
418 | #: remote end.
419 | #: If verify is set to `False`, requests will accept any TLS certificate
420 | #: presented by the server, and will ignore hostname mismatches and/or
421 | #: expired certificates, which will make your application vulnerable to
422 | #: man-in-the-middle (MitM) attacks.
423 | #: Only set this to `False` for testing.
424 | self.verify = True
425 |
426 | #: SSL client certificate default, if String, path to ssl client
427 | #: cert file (.pem). If Tuple, ('cert', 'key') pair.
428 | self.cert = None
429 |
430 | #: Maximum number of redirects allowed. If the request exceeds this
431 | #: limit, a :class:`TooManyRedirects` exception is raised.
432 | #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
433 | #: 30.
434 | self.max_redirects = DEFAULT_REDIRECT_LIMIT
435 |
436 | #: Trust environment settings for proxy configuration, default
437 | #: authentication and similar.
438 | self.trust_env = True
439 |
440 | #: A CookieJar containing all currently outstanding cookies set on this
441 | #: session. By default it is a
442 | #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
443 | #: may be any other ``cookielib.CookieJar`` compatible object.
444 | self.cookies = cookiejar_from_dict({})
445 |
446 | # Default connection adapters.
447 | self.adapters = OrderedDict()
448 | self.mount("https://", HTTPAdapter())
449 | self.mount("http://", HTTPAdapter())
450 |
451 | def __enter__(self):
452 | return self
453 |
454 | def __exit__(self, *args):
455 | self.close()
456 |
457 | def prepare_request(self, request):
458 | """Constructs a :class:`PreparedRequest <PreparedRequest>` for
459 | transmission and returns it. The :class:`PreparedRequest` has settings
460 | merged from the :class:`Request <Request>` instance and those of the
461 | :class:`Session`.
462 |
463 | :param request: :class:`Request` instance to prepare with this
464 | session's settings.
465 | :rtype: requests.PreparedRequest
466 | """
467 | cookies = request.cookies or {}
468 |
469 | # Bootstrap CookieJar.
470 | if not isinstance(cookies, cookielib.CookieJar):
471 | cookies = cookiejar_from_dict(cookies)
472 |
473 | # Merge with session cookies
474 | merged_cookies = merge_cookies(
475 | merge_cookies(RequestsCookieJar(), self.cookies), cookies
476 | )
477 |
478 | # Set environment's basic authentication if not explicitly set.
479 | auth = request.auth
480 | if self.trust_env and not auth and not self.auth:
481 | auth = get_netrc_auth(request.url)
482 |
483 | p = PreparedRequest()
484 | p.prepare(
485 | method=request.method.upper(),
486 | url=request.url,
487 | files=request.files,
488 | data=request.data,
489 | json=request.json,
490 | headers=merge_setting(
491 | request.headers, self.headers, dict_class=CaseInsensitiveDict
492 | ),
493 | params=merge_setting(request.params, self.params),
494 | auth=merge_setting(auth, self.auth),
495 | cookies=merged_cookies,
496 | hooks=merge_hooks(request.hooks, self.hooks),
497 | )
498 | return p
499 |
500 | def request(
501 | self,
502 | method,
503 | url,
504 | params=None,
505 | data=None,
506 | headers=None,
507 | cookies=None,
508 | files=None,
509 | auth=None,
510 | timeout=None,
511 | allow_redirects=True,
512 | proxies=None,
513 | hooks=None,
514 | stream=None,
515 | verify=None,
516 | cert=None,
517 | json=None,
518 | ):
519 | """Constructs a :class:`Request <Request>`, prepares it and sends it.
520 | Returns :class:`Response <Response>` object.
521 |
522 | :param method: method for the new :class:`Request` object.
523 | :param url: URL for the new :class:`Request` object.
524 | :param params: (optional) Dictionary or bytes to be sent in the query
525 | string for the :class:`Request`.
526 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like
527 | object to send in the body of the :class:`Request`.
528 | :param json: (optional) json to send in the body of the
529 | :class:`Request`.
530 | :param headers: (optional) Dictionary of HTTP Headers to send with the
531 | :class:`Request`.
532 | :param cookies: (optional) Dict or CookieJar object to send with the
533 | :class:`Request`.
534 | :param files: (optional) Dictionary of ``'filename': file-like-objects``
535 | for multipart encoding upload.
536 | :param auth: (optional) Auth tuple or callable to enable
537 | Basic/Digest/Custom HTTP Auth.
538 | :param timeout: (optional) How long to wait for the server to send
539 | data before giving up, as a float, or a :ref:`(connect timeout,
540 | read timeout) <timeouts>` tuple.
541 | :type timeout: float or tuple
542 | :param allow_redirects: (optional) Set to True by default.
543 | :type allow_redirects: bool
544 | :param proxies: (optional) Dictionary mapping protocol or protocol and
545 | hostname to the URL of the proxy.
546 | :param hooks: (optional) Dictionary mapping hook name to one event or
547 | list of events, event must be callable.
548 | :param stream: (optional) whether to immediately download the response
549 | content. Defaults to ``False``.
550 | :param verify: (optional) Either a boolean, in which case it controls whether we verify
551 | the server's TLS certificate, or a string, in which case it must be a path
552 | to a CA bundle to use. Defaults to ``True``. When set to
553 | ``False``, requests will accept any TLS certificate presented by
554 | the server, and will ignore hostname mismatches and/or expired
555 | certificates, which will make your application vulnerable to
556 | man-in-the-middle (MitM) attacks. Setting verify to ``False``
557 | may be useful during local development or testing.
558 | :param cert: (optional) if String, path to ssl client cert file (.pem).
559 | If Tuple, ('cert', 'key') pair.
560 | :rtype: requests.Response
561 | """
562 | # Create the Request.
563 | req = Request(
564 | method=method.upper(),
565 | url=url,
566 | headers=headers,
567 | files=files,
568 | data=data or {},
569 | json=json,
570 | params=params or {},
571 | auth=auth,
572 | cookies=cookies,
573 | hooks=hooks,
574 | )
575 | prep = self.prepare_request(req)
576 |
577 | proxies = proxies or {}
578 |
579 | settings = self.merge_environment_settings(
580 | prep.url, proxies, stream, verify, cert
581 | )
582 |
583 | # Send the request.
584 | send_kwargs = {
585 | "timeout": timeout,
586 | "allow_redirects": allow_redirects,
587 | }
588 | send_kwargs.update(settings)
589 | resp = self.send(prep, **send_kwargs)
590 |
591 | return resp
592 |
593 | def get(self, url, **kwargs):
594 | r"""Sends a GET request. Returns :class:`Response` object.
595 |
596 | :param url: URL for the new :class:`Request` object.
597 | :param \*\*kwargs: Optional arguments that ``request`` takes.
598 | :rtype: requests.Response
599 | """
600 |
601 | kwargs.setdefault("allow_redirects", True)
602 | return self.request("GET", url, **kwargs)
603 |
604 | def options(self, url, **kwargs):
605 | r"""Sends a OPTIONS request. Returns :class:`Response` object.
606 |
607 | :param url: URL for the new :class:`Request` object.
608 | :param \*\*kwargs: Optional arguments that ``request`` takes.
609 | :rtype: requests.Response
610 | """
611 |
612 | kwargs.setdefault("allow_redirects", True)
613 | return self.request("OPTIONS", url, **kwargs)
614 |
615 | def head(self, url, **kwargs):
616 | r"""Sends a HEAD request. Returns :class:`Response` object.
617 |
618 | :param url: URL for the new :class:`Request` object.
619 | :param \*\*kwargs: Optional arguments that ``request`` takes.
620 | :rtype: requests.Response
621 | """
622 |
623 | kwargs.setdefault("allow_redirects", False)
624 | return self.request("HEAD", url, **kwargs)
625 |
626 | def post(self, url, data=None, json=None, **kwargs):
627 | r"""Sends a POST request. Returns :class:`Response` object.
628 |
629 | :param url: URL for the new :class:`Request` object.
630 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like
631 | object to send in the body of the :class:`Request`.
632 | :param json: (optional) json to send in the body of the :class:`Request`.
633 | :param \*\*kwargs: Optional arguments that ``request`` takes.
634 | :rtype: requests.Response
635 | """
636 |
637 | return self.request("POST", url, data=data, json=json, **kwargs)
638 |
639 | def put(self, url, data=None, **kwargs):
640 | r"""Sends a PUT request. Returns :class:`Response` object.
641 |
642 | :param url: URL for the new :class:`Request` object.
643 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like
644 | object to send in the body of the :class:`Request`.
645 | :param \*\*kwargs: Optional arguments that ``request`` takes.
646 | :rtype: requests.Response
647 | """
648 |
649 | return self.request("PUT", url, data=data, **kwargs)
650 |
651 | def patch(self, url, data=None, **kwargs):
652 | r"""Sends a PATCH request. Returns :class:`Response` object.
653 |
654 | :param url: URL for the new :class:`Request` object.
655 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like
656 | object to send in the body of the :class:`Request`.
657 | :param \*\*kwargs: Optional arguments that ``request`` takes.
658 | :rtype: requests.Response
659 | """
660 |
661 | return self.request("PATCH", url, data=data, **kwargs)
662 |
663 | def delete(self, url, **kwargs):
664 | r"""Sends a DELETE request. Returns :class:`Response` object.
665 |
666 | :param url: URL for the new :class:`Request` object.
667 | :param \*\*kwargs: Optional arguments that ``request`` takes.
668 | :rtype: requests.Response
669 | """
670 |
671 | return self.request("DELETE", url, **kwargs)
672 |
673 | def send(self, request, **kwargs):
674 | """Send a given PreparedRequest.
675 |
676 | :rtype: requests.Response
677 | """
678 | # Set defaults that the hooks can utilize to ensure they always have
679 | # the correct parameters to reproduce the previous request.
680 | kwargs.setdefault("stream", self.stream)
681 | kwargs.setdefault("verify", self.verify)
682 | kwargs.setdefault("cert", self.cert)
683 | if "proxies" not in kwargs:
684 | kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
685 |
686 | # It's possible that users might accidentally send a Request object.
687 | # Guard against that specific failure case.
688 | if isinstance(request, Request):
689 | raise ValueError("You can only send PreparedRequests.")
690 |
691 | # Set up variables needed for resolve_redirects and dispatching of hooks
692 | allow_redirects = kwargs.pop("allow_redirects", True)
693 | stream = kwargs.get("stream")
694 | hooks = request.hooks
695 |
696 | # Get the appropriate adapter to use
697 | adapter = self.get_adapter(url=request.url)
698 |
699 | # Start time (approximately) of the request
700 | start = preferred_clock()
701 |
702 | # Send the request
703 | r = adapter.send(request, **kwargs)
704 |
705 | # Total elapsed time of the request (approximately)
706 | elapsed = preferred_clock() - start
707 | r.elapsed = timedelta(seconds=elapsed)
708 |
709 | # Response manipulation hooks
710 | r = dispatch_hook("response", hooks, r, **kwargs)
711 |
712 | # Persist cookies
713 | if r.history:
714 | # If the hooks create history then we want those cookies too
715 | for resp in r.history:
716 | extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
717 |
718 | extract_cookies_to_jar(self.cookies, request, r.raw)
719 |
720 | # Resolve redirects if allowed.
721 | if allow_redirects:
722 | # Redirect resolving generator.
723 | gen = self.resolve_redirects(r, request, **kwargs)
724 | history = [resp for resp in gen]
725 | else:
726 | history = []
727 |
728 | # Shuffle things around if there's history.
729 | if history:
730 | # Insert the first (original) request at the start
731 | history.insert(0, r)
732 | # Get the last request made
733 | r = history.pop()
734 | r.history = history
735 |
736 | # If redirects aren't being followed, store the response on the Request for Response.next().
737 | if not allow_redirects:
738 | try:
739 | r._next = next(
740 | self.resolve_redirects(r, request, yield_requests=True, **kwargs)
741 | )
742 | except StopIteration:
743 | pass
744 |
745 | if not stream:
746 | r.content
747 |
748 | return r
749 |
750 | def merge_environment_settings(self, url, proxies, stream, verify, cert):
751 | """
752 | Check the environment and merge it with some settings.
753 |
754 | :rtype: dict
755 | """
756 | # Gather clues from the surrounding environment.
757 | if self.trust_env:
758 | # Set environment's proxies.
759 | no_proxy = proxies.get("no_proxy") if proxies is not None else None
760 | env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
761 | for k, v in env_proxies.items():
762 | proxies.setdefault(k, v)
763 |
764 | # Look for requests environment configuration
765 | # and be compatible with cURL.
766 | if verify is True or verify is None:
767 | verify = (
768 | os.environ.get("REQUESTS_CA_BUNDLE")
769 | or os.environ.get("CURL_CA_BUNDLE")
770 | or verify
771 | )
772 |
773 | # Merge all the kwargs.
774 | proxies = merge_setting(proxies, self.proxies)
775 | stream = merge_setting(stream, self.stream)
776 | verify = merge_setting(verify, self.verify)
777 | cert = merge_setting(cert, self.cert)
778 |
779 | return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
780 |
781 | def get_adapter(self, url):
782 | """
783 | Returns the appropriate connection adapter for the given URL.
784 |
785 | :rtype: requests.adapters.BaseAdapter
786 | """
787 | for prefix, adapter in self.adapters.items():
788 | if url.lower().startswith(prefix.lower()):
789 | return adapter
790 |
791 | # Nothing matches :-/
792 | raise InvalidSchema(f"No connection adapters were found for {url!r}")
793 |
794 | def close(self):
795 | """Closes all adapters and as such the session"""
796 | for v in self.adapters.values():
797 | v.close()
798 |
799 | def mount(self, prefix, adapter):
800 | """Registers a connection adapter to a prefix.
801 |
802 | Adapters are sorted in descending order by prefix length.
803 | """
804 | self.adapters[prefix] = adapter
805 | keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
806 |
807 | for key in keys_to_move:
808 | self.adapters[key] = self.adapters.pop(key)
809 |
810 | def __getstate__(self):
811 | state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
812 | return state
813 |
814 | def __setstate__(self, state):
815 | for attr, value in state.items():
816 | setattr(self, attr, value)
817 |
818 |
819 | def session():
820 | """
821 | Returns a :class:`Session` for context-management.
822 |
823 | .. deprecated:: 1.0.0
824 |
825 | This method has been deprecated since version 1.0.0 and is only kept for
826 | backwards compatibility. New code should use :class:`~requests.sessions.Session`
827 | to create a session. This may be removed at a future date.
828 |
829 | :rtype: Session
830 | """
831 | return Session()
832 |
```