This is page 142 of 168. Use http://codebase.md/romanshablio/mcp_server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .DS_Store
├── .venv
│ ├── __pycache__
│ │ └── hello.cpython-312.pyc
│ ├── bin
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── Activate.ps1
│ │ ├── flask
│ │ ├── normalizer
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.12
│ │ ├── python
│ │ ├── python3
│ │ └── python3.12
│ ├── hello.py
│ ├── lib
│ │ └── python3.12
│ │ └── site-packages
│ │ ├── beautifulsoup4-4.12.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ ├── AUTHORS
│ │ │ │ └── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── blinker
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _utilities.cpython-312.pyc
│ │ │ │ └── base.cpython-312.pyc
│ │ │ ├── _utilities.py
│ │ │ ├── base.py
│ │ │ └── py.typed
│ │ ├── blinker-1.8.2.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── bs4
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── css.cpython-312.pyc
│ │ │ │ ├── dammit.cpython-312.pyc
│ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ ├── element.cpython-312.pyc
│ │ │ │ └── formatter.cpython-312.pyc
│ │ │ ├── builder
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _html5lib.cpython-312.pyc
│ │ │ │ │ ├── _htmlparser.cpython-312.pyc
│ │ │ │ │ └── _lxml.cpython-312.pyc
│ │ │ │ ├── _html5lib.py
│ │ │ │ ├── _htmlparser.py
│ │ │ │ └── _lxml.py
│ │ │ ├── css.py
│ │ │ ├── dammit.py
│ │ │ ├── diagnose.py
│ │ │ ├── element.py
│ │ │ ├── formatter.py
│ │ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── test_builder_registry.cpython-312.pyc
│ │ │ │ ├── test_builder.cpython-312.pyc
│ │ │ │ ├── test_css.cpython-312.pyc
│ │ │ │ ├── test_dammit.cpython-312.pyc
│ │ │ │ ├── test_docs.cpython-312.pyc
│ │ │ │ ├── test_element.cpython-312.pyc
│ │ │ │ ├── test_formatter.cpython-312.pyc
│ │ │ │ ├── test_fuzz.cpython-312.pyc
│ │ │ │ ├── test_html5lib.cpython-312.pyc
│ │ │ │ ├── test_htmlparser.cpython-312.pyc
│ │ │ │ ├── test_lxml.cpython-312.pyc
│ │ │ │ ├── test_navigablestring.cpython-312.pyc
│ │ │ │ ├── test_pageelement.cpython-312.pyc
│ │ │ │ ├── test_soup.cpython-312.pyc
│ │ │ │ ├── test_tag.cpython-312.pyc
│ │ │ │ └── test_tree.cpython-312.pyc
│ │ │ ├── fuzz
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase
│ │ │ │ ├── clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase
│ │ │ │ ├── crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase
│ │ │ │ └── crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase
│ │ │ ├── test_builder_registry.py
│ │ │ ├── test_builder.py
│ │ │ ├── test_css.py
│ │ │ ├── test_dammit.py
│ │ │ ├── test_docs.py
│ │ │ ├── test_element.py
│ │ │ ├── test_formatter.py
│ │ │ ├── test_fuzz.py
│ │ │ ├── test_html5lib.py
│ │ │ ├── test_htmlparser.py
│ │ │ ├── test_lxml.py
│ │ │ ├── test_navigablestring.py
│ │ │ ├── test_pageelement.py
│ │ │ ├── test_soup.py
│ │ │ ├── test_tag.py
│ │ │ └── test_tree.py
│ │ ├── certifi
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── core.cpython-312.pyc
│ │ │ ├── cacert.pem
│ │ │ ├── core.py
│ │ │ └── py.typed
│ │ ├── certifi-2024.8.30.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── charset_normalizer
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── cd.cpython-312.pyc
│ │ │ │ ├── constant.cpython-312.pyc
│ │ │ │ ├── legacy.cpython-312.pyc
│ │ │ │ ├── md.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── version.cpython-312.pyc
│ │ │ ├── api.py
│ │ │ ├── cd.py
│ │ │ ├── cli
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __main__.py
│ │ │ │ └── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── __main__.cpython-312.pyc
│ │ │ ├── constant.py
│ │ │ ├── legacy.py
│ │ │ ├── md__mypyc.cpython-312-darwin.so
│ │ │ ├── md.cpython-312-darwin.so
│ │ │ ├── md.py
│ │ │ ├── models.py
│ │ │ ├── py.typed
│ │ │ ├── utils.py
│ │ │ └── version.py
│ │ ├── charset_normalizer-3.4.0.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── click
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ ├── _termui_impl.cpython-312.pyc
│ │ │ │ ├── _textwrap.cpython-312.pyc
│ │ │ │ ├── _winconsole.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── decorators.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formatting.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── shell_completion.cpython-312.pyc
│ │ │ │ ├── termui.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── types.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── _compat.py
│ │ │ ├── _termui_impl.py
│ │ │ ├── _textwrap.py
│ │ │ ├── _winconsole.py
│ │ │ ├── core.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── formatting.py
│ │ │ ├── globals.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── shell_completion.py
│ │ │ ├── termui.py
│ │ │ ├── testing.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ ├── click-8.1.7.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.rst
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── fake_useragent
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ ├── fake.cpython-312.pyc
│ │ │ │ ├── log.cpython-312.pyc
│ │ │ │ ├── settings.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── data
│ │ │ │ └── browsers.json
│ │ │ ├── errors.py
│ │ │ ├── fake.py
│ │ │ ├── log.py
│ │ │ ├── settings.py
│ │ │ └── utils.py
│ │ ├── fake_useragent-1.5.1.dist-info
│ │ │ ├── AUTHORS
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── flask
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ ├── cli.cpython-312.pyc
│ │ │ │ ├── config.cpython-312.pyc
│ │ │ │ ├── ctx.cpython-312.pyc
│ │ │ │ ├── debughelpers.cpython-312.pyc
│ │ │ │ ├── globals.cpython-312.pyc
│ │ │ │ ├── helpers.cpython-312.pyc
│ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── signals.cpython-312.pyc
│ │ │ │ ├── templating.cpython-312.pyc
│ │ │ │ ├── testing.cpython-312.pyc
│ │ │ │ ├── typing.cpython-312.pyc
│ │ │ │ ├── views.cpython-312.pyc
│ │ │ │ └── wrappers.cpython-312.pyc
│ │ │ ├── app.py
│ │ │ ├── blueprints.py
│ │ │ ├── cli.py
│ │ │ ├── config.py
│ │ │ ├── ctx.py
│ │ │ ├── debughelpers.py
│ │ │ ├── globals.py
│ │ │ ├── helpers.py
│ │ │ ├── json
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ └── tag.cpython-312.pyc
│ │ │ │ ├── provider.py
│ │ │ │ └── tag.py
│ │ │ ├── logging.py
│ │ │ ├── py.typed
│ │ │ ├── sansio
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── app.cpython-312.pyc
│ │ │ │ │ ├── blueprints.cpython-312.pyc
│ │ │ │ │ └── scaffold.cpython-312.pyc
│ │ │ │ ├── app.py
│ │ │ │ ├── blueprints.py
│ │ │ │ ├── README.md
│ │ │ │ └── scaffold.py
│ │ │ ├── sessions.py
│ │ │ ├── signals.py
│ │ │ ├── templating.py
│ │ │ ├── testing.py
│ │ │ ├── typing.py
│ │ │ ├── views.py
│ │ │ └── wrappers.py
│ │ ├── flask-3.0.3.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ └── WHEEL
│ │ ├── idna
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ ├── codec.py
│ │ │ ├── compat.py
│ │ │ ├── core.py
│ │ │ ├── idnadata.py
│ │ │ ├── intranges.py
│ │ │ ├── package_data.py
│ │ │ ├── py.typed
│ │ │ └── uts46data.py
│ │ ├── idna-3.10.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── itsdangerous
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ ├── exc.cpython-312.pyc
│ │ │ │ ├── serializer.cpython-312.pyc
│ │ │ │ ├── signer.cpython-312.pyc
│ │ │ │ ├── timed.cpython-312.pyc
│ │ │ │ └── url_safe.cpython-312.pyc
│ │ │ ├── _json.py
│ │ │ ├── encoding.py
│ │ │ ├── exc.py
│ │ │ ├── py.typed
│ │ │ ├── serializer.py
│ │ │ ├── signer.py
│ │ │ ├── timed.py
│ │ │ └── url_safe.py
│ │ ├── itsdangerous-2.2.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── jinja2
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _identifier.cpython-312.pyc
│ │ │ │ ├── async_utils.cpython-312.pyc
│ │ │ │ ├── bccache.cpython-312.pyc
│ │ │ │ ├── compiler.cpython-312.pyc
│ │ │ │ ├── constants.cpython-312.pyc
│ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ ├── defaults.cpython-312.pyc
│ │ │ │ ├── environment.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ ├── filters.cpython-312.pyc
│ │ │ │ ├── idtracking.cpython-312.pyc
│ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ ├── loaders.cpython-312.pyc
│ │ │ │ ├── meta.cpython-312.pyc
│ │ │ │ ├── nativetypes.cpython-312.pyc
│ │ │ │ ├── nodes.cpython-312.pyc
│ │ │ │ ├── optimizer.cpython-312.pyc
│ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ ├── runtime.cpython-312.pyc
│ │ │ │ ├── sandbox.cpython-312.pyc
│ │ │ │ ├── tests.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── visitor.cpython-312.pyc
│ │ │ ├── _identifier.py
│ │ │ ├── async_utils.py
│ │ │ ├── bccache.py
│ │ │ ├── compiler.py
│ │ │ ├── constants.py
│ │ │ ├── debug.py
│ │ │ ├── defaults.py
│ │ │ ├── environment.py
│ │ │ ├── exceptions.py
│ │ │ ├── ext.py
│ │ │ ├── filters.py
│ │ │ ├── idtracking.py
│ │ │ ├── lexer.py
│ │ │ ├── loaders.py
│ │ │ ├── meta.py
│ │ │ ├── nativetypes.py
│ │ │ ├── nodes.py
│ │ │ ├── optimizer.py
│ │ │ ├── parser.py
│ │ │ ├── py.typed
│ │ │ ├── runtime.py
│ │ │ ├── sandbox.py
│ │ │ ├── tests.py
│ │ │ ├── utils.py
│ │ │ └── visitor.py
│ │ ├── jinja2-3.1.4.dist-info
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── lxml
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _elementpath.cpython-312.pyc
│ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ ├── cssselect.cpython-312.pyc
│ │ │ │ ├── doctestcompare.cpython-312.pyc
│ │ │ │ ├── ElementInclude.cpython-312.pyc
│ │ │ │ ├── pyclasslookup.cpython-312.pyc
│ │ │ │ ├── sax.cpython-312.pyc
│ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ ├── _elementpath.cpython-312-darwin.so
│ │ │ ├── _elementpath.py
│ │ │ ├── apihelpers.pxi
│ │ │ ├── builder.cpython-312-darwin.so
│ │ │ ├── builder.py
│ │ │ ├── classlookup.pxi
│ │ │ ├── cleanup.pxi
│ │ │ ├── cssselect.py
│ │ │ ├── debug.pxi
│ │ │ ├── docloader.pxi
│ │ │ ├── doctestcompare.py
│ │ │ ├── dtd.pxi
│ │ │ ├── ElementInclude.py
│ │ │ ├── etree_api.h
│ │ │ ├── etree.cpython-312-darwin.so
│ │ │ ├── etree.h
│ │ │ ├── etree.pyx
│ │ │ ├── extensions.pxi
│ │ │ ├── html
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── _diffcommand.cpython-312.pyc
│ │ │ │ │ ├── _html5builder.cpython-312.pyc
│ │ │ │ │ ├── _setmixin.cpython-312.pyc
│ │ │ │ │ ├── builder.cpython-312.pyc
│ │ │ │ │ ├── clean.cpython-312.pyc
│ │ │ │ │ ├── defs.cpython-312.pyc
│ │ │ │ │ ├── diff.cpython-312.pyc
│ │ │ │ │ ├── ElementSoup.cpython-312.pyc
│ │ │ │ │ ├── formfill.cpython-312.pyc
│ │ │ │ │ ├── html5parser.cpython-312.pyc
│ │ │ │ │ ├── soupparser.cpython-312.pyc
│ │ │ │ │ └── usedoctest.cpython-312.pyc
│ │ │ │ ├── _diffcommand.py
│ │ │ │ ├── _html5builder.py
│ │ │ │ ├── _setmixin.py
│ │ │ │ ├── builder.py
│ │ │ │ ├── clean.py
│ │ │ │ ├── defs.py
│ │ │ │ ├── diff.cpython-312-darwin.so
│ │ │ │ ├── diff.py
│ │ │ │ ├── ElementSoup.py
│ │ │ │ ├── formfill.py
│ │ │ │ ├── html5parser.py
│ │ │ │ ├── soupparser.py
│ │ │ │ └── usedoctest.py
│ │ │ ├── includes
│ │ │ │ ├── __init__.pxd
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── c14n.pxd
│ │ │ │ ├── config.pxd
│ │ │ │ ├── dtdvalid.pxd
│ │ │ │ ├── etree_defs.h
│ │ │ │ ├── etreepublic.pxd
│ │ │ │ ├── extlibs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── libcharset.h
│ │ │ │ │ ├── localcharset.h
│ │ │ │ │ ├── zconf.h
│ │ │ │ │ └── zlib.h
│ │ │ │ ├── htmlparser.pxd
│ │ │ │ ├── libexslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── exslt.h
│ │ │ │ │ ├── exsltconfig.h
│ │ │ │ │ └── exsltexports.h
│ │ │ │ ├── libxml
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── c14n.h
│ │ │ │ │ ├── catalog.h
│ │ │ │ │ ├── chvalid.h
│ │ │ │ │ ├── debugXML.h
│ │ │ │ │ ├── dict.h
│ │ │ │ │ ├── encoding.h
│ │ │ │ │ ├── entities.h
│ │ │ │ │ ├── globals.h
│ │ │ │ │ ├── hash.h
│ │ │ │ │ ├── HTMLparser.h
│ │ │ │ │ ├── HTMLtree.h
│ │ │ │ │ ├── list.h
│ │ │ │ │ ├── nanoftp.h
│ │ │ │ │ ├── nanohttp.h
│ │ │ │ │ ├── parser.h
│ │ │ │ │ ├── parserInternals.h
│ │ │ │ │ ├── relaxng.h
│ │ │ │ │ ├── SAX.h
│ │ │ │ │ ├── SAX2.h
│ │ │ │ │ ├── schemasInternals.h
│ │ │ │ │ ├── schematron.h
│ │ │ │ │ ├── threads.h
│ │ │ │ │ ├── tree.h
│ │ │ │ │ ├── uri.h
│ │ │ │ │ ├── valid.h
│ │ │ │ │ ├── xinclude.h
│ │ │ │ │ ├── xlink.h
│ │ │ │ │ ├── xmlautomata.h
│ │ │ │ │ ├── xmlerror.h
│ │ │ │ │ ├── xmlexports.h
│ │ │ │ │ ├── xmlIO.h
│ │ │ │ │ ├── xmlmemory.h
│ │ │ │ │ ├── xmlmodule.h
│ │ │ │ │ ├── xmlreader.h
│ │ │ │ │ ├── xmlregexp.h
│ │ │ │ │ ├── xmlsave.h
│ │ │ │ │ ├── xmlschemas.h
│ │ │ │ │ ├── xmlschemastypes.h
│ │ │ │ │ ├── xmlstring.h
│ │ │ │ │ ├── xmlunicode.h
│ │ │ │ │ ├── xmlversion.h
│ │ │ │ │ ├── xmlwriter.h
│ │ │ │ │ ├── xpath.h
│ │ │ │ │ ├── xpathInternals.h
│ │ │ │ │ └── xpointer.h
│ │ │ │ ├── libxslt
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── attributes.h
│ │ │ │ │ ├── documents.h
│ │ │ │ │ ├── extensions.h
│ │ │ │ │ ├── extra.h
│ │ │ │ │ ├── functions.h
│ │ │ │ │ ├── imports.h
│ │ │ │ │ ├── keys.h
│ │ │ │ │ ├── namespaces.h
│ │ │ │ │ ├── numbersInternals.h
│ │ │ │ │ ├── pattern.h
│ │ │ │ │ ├── preproc.h
│ │ │ │ │ ├── security.h
│ │ │ │ │ ├── templates.h
│ │ │ │ │ ├── transform.h
│ │ │ │ │ ├── variables.h
│ │ │ │ │ ├── xslt.h
│ │ │ │ │ ├── xsltconfig.h
│ │ │ │ │ ├── xsltexports.h
│ │ │ │ │ ├── xsltInternals.h
│ │ │ │ │ ├── xsltlocale.h
│ │ │ │ │ └── xsltutils.h
│ │ │ │ ├── lxml-version.h
│ │ │ │ ├── relaxng.pxd
│ │ │ │ ├── schematron.pxd
│ │ │ │ ├── tree.pxd
│ │ │ │ ├── uri.pxd
│ │ │ │ ├── xinclude.pxd
│ │ │ │ ├── xmlerror.pxd
│ │ │ │ ├── xmlparser.pxd
│ │ │ │ ├── xmlschema.pxd
│ │ │ │ ├── xpath.pxd
│ │ │ │ └── xslt.pxd
│ │ │ ├── isoschematron
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ └── resources
│ │ │ │ ├── rng
│ │ │ │ │ └── iso-schematron.rng
│ │ │ │ └── xsl
│ │ │ │ ├── iso-schematron-xslt1
│ │ │ │ │ ├── iso_abstract_expand.xsl
│ │ │ │ │ ├── iso_dsdl_include.xsl
│ │ │ │ │ ├── iso_schematron_message.xsl
│ │ │ │ │ ├── iso_schematron_skeleton_for_xslt1.xsl
│ │ │ │ │ ├── iso_svrl_for_xslt1.xsl
│ │ │ │ │ └── readme.txt
│ │ │ │ ├── RNG2Schtrn.xsl
│ │ │ │ └── XSD2Schtrn.xsl
│ │ │ ├── iterparse.pxi
│ │ │ ├── lxml.etree_api.h
│ │ │ ├── lxml.etree.h
│ │ │ ├── nsclasses.pxi
│ │ │ ├── objectify.cpython-312-darwin.so
│ │ │ ├── objectify.pyx
│ │ │ ├── objectpath.pxi
│ │ │ ├── parser.pxi
│ │ │ ├── parsertarget.pxi
│ │ │ ├── proxy.pxi
│ │ │ ├── public-api.pxi
│ │ │ ├── pyclasslookup.py
│ │ │ ├── readonlytree.pxi
│ │ │ ├── relaxng.pxi
│ │ │ ├── sax.cpython-312-darwin.so
│ │ │ ├── sax.py
│ │ │ ├── saxparser.pxi
│ │ │ ├── schematron.pxi
│ │ │ ├── serializer.pxi
│ │ │ ├── usedoctest.py
│ │ │ ├── xinclude.pxi
│ │ │ ├── xmlerror.pxi
│ │ │ ├── xmlid.pxi
│ │ │ ├── xmlschema.pxi
│ │ │ ├── xpath.pxi
│ │ │ ├── xslt.pxi
│ │ │ └── xsltext.pxi
│ │ ├── lxml-5.3.0.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── LICENSES.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── markupsafe
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ └── _native.cpython-312.pyc
│ │ │ ├── _native.py
│ │ │ ├── _speedups.c
│ │ │ ├── _speedups.cpython-312-darwin.so
│ │ │ ├── _speedups.pyi
│ │ │ └── py.typed
│ │ ├── MarkupSafe-3.0.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── pip
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── __pip-runner__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ └── __pip-runner__.cpython-312.pyc
│ │ │ ├── _internal
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── build_env.cpython-312.pyc
│ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ ├── pyproject.cpython-312.pyc
│ │ │ │ │ ├── self_outdated_check.cpython-312.pyc
│ │ │ │ │ └── wheel_builder.cpython-312.pyc
│ │ │ │ ├── build_env.py
│ │ │ │ ├── cache.py
│ │ │ │ ├── cli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── autocompletion.cpython-312.pyc
│ │ │ │ │ │ ├── base_command.cpython-312.pyc
│ │ │ │ │ │ ├── cmdoptions.cpython-312.pyc
│ │ │ │ │ │ ├── command_context.cpython-312.pyc
│ │ │ │ │ │ ├── index_command.cpython-312.pyc
│ │ │ │ │ │ ├── main_parser.cpython-312.pyc
│ │ │ │ │ │ ├── main.cpython-312.pyc
│ │ │ │ │ │ ├── parser.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bars.cpython-312.pyc
│ │ │ │ │ │ ├── req_command.cpython-312.pyc
│ │ │ │ │ │ ├── spinners.cpython-312.pyc
│ │ │ │ │ │ └── status_codes.cpython-312.pyc
│ │ │ │ │ ├── autocompletion.py
│ │ │ │ │ ├── base_command.py
│ │ │ │ │ ├── cmdoptions.py
│ │ │ │ │ ├── command_context.py
│ │ │ │ │ ├── index_command.py
│ │ │ │ │ ├── main_parser.py
│ │ │ │ │ ├── main.py
│ │ │ │ │ ├── parser.py
│ │ │ │ │ ├── progress_bars.py
│ │ │ │ │ ├── req_command.py
│ │ │ │ │ ├── spinners.py
│ │ │ │ │ └── status_codes.py
│ │ │ │ ├── commands
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── completion.cpython-312.pyc
│ │ │ │ │ │ ├── configuration.cpython-312.pyc
│ │ │ │ │ │ ├── debug.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ ├── hash.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── inspect.cpython-312.pyc
│ │ │ │ │ │ ├── install.cpython-312.pyc
│ │ │ │ │ │ ├── list.cpython-312.pyc
│ │ │ │ │ │ ├── search.cpython-312.pyc
│ │ │ │ │ │ ├── show.cpython-312.pyc
│ │ │ │ │ │ ├── uninstall.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── completion.py
│ │ │ │ │ ├── configuration.py
│ │ │ │ │ ├── debug.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── hash.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── inspect.py
│ │ │ │ │ ├── install.py
│ │ │ │ │ ├── list.py
│ │ │ │ │ ├── search.py
│ │ │ │ │ ├── show.py
│ │ │ │ │ ├── uninstall.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── configuration.py
│ │ │ │ ├── distributions
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── installed.cpython-312.pyc
│ │ │ │ │ │ ├── sdist.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── installed.py
│ │ │ │ │ ├── sdist.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── index
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── collector.cpython-312.pyc
│ │ │ │ │ │ ├── package_finder.cpython-312.pyc
│ │ │ │ │ │ └── sources.cpython-312.pyc
│ │ │ │ │ ├── collector.py
│ │ │ │ │ ├── package_finder.py
│ │ │ │ │ └── sources.py
│ │ │ │ ├── locations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _distutils.cpython-312.pyc
│ │ │ │ │ │ ├── _sysconfig.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── _distutils.py
│ │ │ │ │ ├── _sysconfig.py
│ │ │ │ │ └── base.py
│ │ │ │ ├── main.py
│ │ │ │ ├── metadata
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _json.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ └── pkg_resources.cpython-312.pyc
│ │ │ │ │ ├── _json.py
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── importlib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ │ ├── _dists.cpython-312.pyc
│ │ │ │ │ │ │ └── _envs.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.py
│ │ │ │ │ │ ├── _dists.py
│ │ │ │ │ │ └── _envs.py
│ │ │ │ │ └── pkg_resources.py
│ │ │ │ ├── models
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── candidate.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url.cpython-312.pyc
│ │ │ │ │ │ ├── format_control.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── installation_report.cpython-312.pyc
│ │ │ │ │ │ ├── link.cpython-312.pyc
│ │ │ │ │ │ ├── scheme.cpython-312.pyc
│ │ │ │ │ │ ├── search_scope.cpython-312.pyc
│ │ │ │ │ │ ├── selection_prefs.cpython-312.pyc
│ │ │ │ │ │ ├── target_python.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── candidate.py
│ │ │ │ │ ├── direct_url.py
│ │ │ │ │ ├── format_control.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── installation_report.py
│ │ │ │ │ ├── link.py
│ │ │ │ │ ├── scheme.py
│ │ │ │ │ ├── search_scope.py
│ │ │ │ │ ├── selection_prefs.py
│ │ │ │ │ ├── target_python.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── network
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── download.cpython-312.pyc
│ │ │ │ │ │ ├── lazy_wheel.cpython-312.pyc
│ │ │ │ │ │ ├── session.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── xmlrpc.cpython-312.pyc
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── download.py
│ │ │ │ │ ├── lazy_wheel.py
│ │ │ │ │ ├── session.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── xmlrpc.py
│ │ │ │ ├── operations
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── check.cpython-312.pyc
│ │ │ │ │ │ ├── freeze.cpython-312.pyc
│ │ │ │ │ │ └── prepare.cpython-312.pyc
│ │ │ │ │ ├── build
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── build_tracker.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata_legacy.cpython-312.pyc
│ │ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_editable.cpython-312.pyc
│ │ │ │ │ │ │ ├── wheel_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── build_tracker.py
│ │ │ │ │ │ ├── metadata_editable.py
│ │ │ │ │ │ ├── metadata_legacy.py
│ │ │ │ │ │ ├── metadata.py
│ │ │ │ │ │ ├── wheel_editable.py
│ │ │ │ │ │ ├── wheel_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ ├── check.py
│ │ │ │ │ ├── freeze.py
│ │ │ │ │ ├── install
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── editable_legacy.cpython-312.pyc
│ │ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ │ ├── editable_legacy.py
│ │ │ │ │ │ └── wheel.py
│ │ │ │ │ └── prepare.py
│ │ │ │ ├── pyproject.py
│ │ │ │ ├── req
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── constructors.cpython-312.pyc
│ │ │ │ │ │ ├── req_file.cpython-312.pyc
│ │ │ │ │ │ ├── req_install.cpython-312.pyc
│ │ │ │ │ │ ├── req_set.cpython-312.pyc
│ │ │ │ │ │ └── req_uninstall.cpython-312.pyc
│ │ │ │ │ ├── constructors.py
│ │ │ │ │ ├── req_file.py
│ │ │ │ │ ├── req_install.py
│ │ │ │ │ ├── req_set.py
│ │ │ │ │ └── req_uninstall.py
│ │ │ │ ├── resolution
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── base.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ │ └── resolver.py
│ │ │ │ │ └── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── base.cpython-312.pyc
│ │ │ │ │ │ ├── candidates.cpython-312.pyc
│ │ │ │ │ │ ├── factory.cpython-312.pyc
│ │ │ │ │ │ ├── found_candidates.cpython-312.pyc
│ │ │ │ │ │ ├── provider.cpython-312.pyc
│ │ │ │ │ │ ├── reporter.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ └── resolver.cpython-312.pyc
│ │ │ │ │ ├── base.py
│ │ │ │ │ ├── candidates.py
│ │ │ │ │ ├── factory.py
│ │ │ │ │ ├── found_candidates.py
│ │ │ │ │ ├── provider.py
│ │ │ │ │ ├── reporter.py
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ └── resolver.py
│ │ │ │ ├── self_outdated_check.py
│ │ │ │ ├── utils
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _jaraco_text.cpython-312.pyc
│ │ │ │ │ │ ├── _log.cpython-312.pyc
│ │ │ │ │ │ ├── appdirs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── compatibility_tags.cpython-312.pyc
│ │ │ │ │ │ ├── datetime.cpython-312.pyc
│ │ │ │ │ │ ├── deprecation.cpython-312.pyc
│ │ │ │ │ │ ├── direct_url_helpers.cpython-312.pyc
│ │ │ │ │ │ ├── egg_link.cpython-312.pyc
│ │ │ │ │ │ ├── encoding.cpython-312.pyc
│ │ │ │ │ │ ├── entrypoints.cpython-312.pyc
│ │ │ │ │ │ ├── filesystem.cpython-312.pyc
│ │ │ │ │ │ ├── filetypes.cpython-312.pyc
│ │ │ │ │ │ ├── glibc.cpython-312.pyc
│ │ │ │ │ │ ├── hashes.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── misc.cpython-312.pyc
│ │ │ │ │ │ ├── packaging.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── setuptools_build.cpython-312.pyc
│ │ │ │ │ │ ├── subprocess.cpython-312.pyc
│ │ │ │ │ │ ├── temp_dir.cpython-312.pyc
│ │ │ │ │ │ ├── unpacking.cpython-312.pyc
│ │ │ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ │ │ ├── virtualenv.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── _jaraco_text.py
│ │ │ │ │ ├── _log.py
│ │ │ │ │ ├── appdirs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── compatibility_tags.py
│ │ │ │ │ ├── datetime.py
│ │ │ │ │ ├── deprecation.py
│ │ │ │ │ ├── direct_url_helpers.py
│ │ │ │ │ ├── egg_link.py
│ │ │ │ │ ├── encoding.py
│ │ │ │ │ ├── entrypoints.py
│ │ │ │ │ ├── filesystem.py
│ │ │ │ │ ├── filetypes.py
│ │ │ │ │ ├── glibc.py
│ │ │ │ │ ├── hashes.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── misc.py
│ │ │ │ │ ├── packaging.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── setuptools_build.py
│ │ │ │ │ ├── subprocess.py
│ │ │ │ │ ├── temp_dir.py
│ │ │ │ │ ├── unpacking.py
│ │ │ │ │ ├── urls.py
│ │ │ │ │ ├── virtualenv.py
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── vcs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── bazaar.cpython-312.pyc
│ │ │ │ │ │ ├── git.cpython-312.pyc
│ │ │ │ │ │ ├── mercurial.cpython-312.pyc
│ │ │ │ │ │ ├── subversion.cpython-312.pyc
│ │ │ │ │ │ └── versioncontrol.cpython-312.pyc
│ │ │ │ │ ├── bazaar.py
│ │ │ │ │ ├── git.py
│ │ │ │ │ ├── mercurial.py
│ │ │ │ │ ├── subversion.py
│ │ │ │ │ └── versioncontrol.py
│ │ │ │ └── wheel_builder.py
│ │ │ ├── _vendor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ └── typing_extensions.cpython-312.pyc
│ │ │ │ ├── cachecontrol
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _cmd.cpython-312.pyc
│ │ │ │ │ │ ├── adapter.cpython-312.pyc
│ │ │ │ │ │ ├── cache.cpython-312.pyc
│ │ │ │ │ │ ├── controller.cpython-312.pyc
│ │ │ │ │ │ ├── filewrapper.cpython-312.pyc
│ │ │ │ │ │ ├── heuristics.cpython-312.pyc
│ │ │ │ │ │ ├── serialize.cpython-312.pyc
│ │ │ │ │ │ └── wrapper.cpython-312.pyc
│ │ │ │ │ ├── _cmd.py
│ │ │ │ │ ├── adapter.py
│ │ │ │ │ ├── cache.py
│ │ │ │ │ ├── caches
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── file_cache.cpython-312.pyc
│ │ │ │ │ │ │ └── redis_cache.cpython-312.pyc
│ │ │ │ │ │ ├── file_cache.py
│ │ │ │ │ │ └── redis_cache.py
│ │ │ │ │ ├── controller.py
│ │ │ │ │ ├── filewrapper.py
│ │ │ │ │ ├── heuristics.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── serialize.py
│ │ │ │ │ └── wrapper.py
│ │ │ │ ├── certifi
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── core.cpython-312.pyc
│ │ │ │ │ ├── cacert.pem
│ │ │ │ │ ├── core.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── distlib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── database.cpython-312.pyc
│ │ │ │ │ │ ├── index.cpython-312.pyc
│ │ │ │ │ │ ├── locators.cpython-312.pyc
│ │ │ │ │ │ ├── manifest.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── resources.cpython-312.pyc
│ │ │ │ │ │ ├── scripts.cpython-312.pyc
│ │ │ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── wheel.cpython-312.pyc
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── database.py
│ │ │ │ │ ├── index.py
│ │ │ │ │ ├── locators.py
│ │ │ │ │ ├── manifest.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ ├── scripts.py
│ │ │ │ │ ├── t32.exe
│ │ │ │ │ ├── t64-arm.exe
│ │ │ │ │ ├── t64.exe
│ │ │ │ │ ├── util.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ ├── w32.exe
│ │ │ │ │ ├── w64-arm.exe
│ │ │ │ │ ├── w64.exe
│ │ │ │ │ └── wheel.py
│ │ │ │ ├── distro
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ └── distro.cpython-312.pyc
│ │ │ │ │ ├── distro.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── idna
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── codec.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── core.cpython-312.pyc
│ │ │ │ │ │ ├── idnadata.cpython-312.pyc
│ │ │ │ │ │ ├── intranges.cpython-312.pyc
│ │ │ │ │ │ ├── package_data.cpython-312.pyc
│ │ │ │ │ │ └── uts46data.cpython-312.pyc
│ │ │ │ │ ├── codec.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── core.py
│ │ │ │ │ ├── idnadata.py
│ │ │ │ │ ├── intranges.py
│ │ │ │ │ ├── package_data.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ └── uts46data.py
│ │ │ │ ├── msgpack
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── ext.cpython-312.pyc
│ │ │ │ │ │ └── fallback.cpython-312.pyc
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── ext.py
│ │ │ │ │ └── fallback.py
│ │ │ │ ├── packaging
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _elffile.cpython-312.pyc
│ │ │ │ │ │ ├── _manylinux.cpython-312.pyc
│ │ │ │ │ │ ├── _musllinux.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _structures.cpython-312.pyc
│ │ │ │ │ │ ├── _tokenizer.cpython-312.pyc
│ │ │ │ │ │ ├── markers.cpython-312.pyc
│ │ │ │ │ │ ├── metadata.cpython-312.pyc
│ │ │ │ │ │ ├── requirements.cpython-312.pyc
│ │ │ │ │ │ ├── specifiers.cpython-312.pyc
│ │ │ │ │ │ ├── tags.cpython-312.pyc
│ │ │ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ │ │ └── version.cpython-312.pyc
│ │ │ │ │ ├── _elffile.py
│ │ │ │ │ ├── _manylinux.py
│ │ │ │ │ ├── _musllinux.py
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _structures.py
│ │ │ │ │ ├── _tokenizer.py
│ │ │ │ │ ├── markers.py
│ │ │ │ │ ├── metadata.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── requirements.py
│ │ │ │ │ ├── specifiers.py
│ │ │ │ │ ├── tags.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── version.py
│ │ │ │ ├── pkg_resources
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── __pycache__
│ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ ├── platformdirs
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── android.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── macos.cpython-312.pyc
│ │ │ │ │ │ ├── unix.cpython-312.pyc
│ │ │ │ │ │ ├── version.cpython-312.pyc
│ │ │ │ │ │ └── windows.cpython-312.pyc
│ │ │ │ │ ├── android.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── macos.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── unix.py
│ │ │ │ │ ├── version.py
│ │ │ │ │ └── windows.py
│ │ │ │ ├── pygments
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── cmdline.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── filter.cpython-312.pyc
│ │ │ │ │ │ ├── formatter.cpython-312.pyc
│ │ │ │ │ │ ├── lexer.cpython-312.pyc
│ │ │ │ │ │ ├── modeline.cpython-312.pyc
│ │ │ │ │ │ ├── plugin.cpython-312.pyc
│ │ │ │ │ │ ├── regexopt.cpython-312.pyc
│ │ │ │ │ │ ├── scanner.cpython-312.pyc
│ │ │ │ │ │ ├── sphinxext.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── token.cpython-312.pyc
│ │ │ │ │ │ ├── unistring.cpython-312.pyc
│ │ │ │ │ │ └── util.cpython-312.pyc
│ │ │ │ │ ├── cmdline.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── filter.py
│ │ │ │ │ ├── filters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ └── __pycache__
│ │ │ │ │ │ └── __init__.cpython-312.pyc
│ │ │ │ │ ├── formatter.py
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ ├── bbcode.cpython-312.pyc
│ │ │ │ │ │ │ ├── groff.cpython-312.pyc
│ │ │ │ │ │ │ ├── html.cpython-312.pyc
│ │ │ │ │ │ │ ├── img.cpython-312.pyc
│ │ │ │ │ │ │ ├── irc.cpython-312.pyc
│ │ │ │ │ │ │ ├── latex.cpython-312.pyc
│ │ │ │ │ │ │ ├── other.cpython-312.pyc
│ │ │ │ │ │ │ ├── pangomarkup.cpython-312.pyc
│ │ │ │ │ │ │ ├── rtf.cpython-312.pyc
│ │ │ │ │ │ │ ├── svg.cpython-312.pyc
│ │ │ │ │ │ │ ├── terminal.cpython-312.pyc
│ │ │ │ │ │ │ └── terminal256.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ ├── bbcode.py
│ │ │ │ │ │ ├── groff.py
│ │ │ │ │ │ ├── html.py
│ │ │ │ │ │ ├── img.py
│ │ │ │ │ │ ├── irc.py
│ │ │ │ │ │ ├── latex.py
│ │ │ │ │ │ ├── other.py
│ │ │ │ │ │ ├── pangomarkup.py
│ │ │ │ │ │ ├── rtf.py
│ │ │ │ │ │ ├── svg.py
│ │ │ │ │ │ ├── terminal.py
│ │ │ │ │ │ └── terminal256.py
│ │ │ │ │ ├── lexer.py
│ │ │ │ │ ├── lexers
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _mapping.cpython-312.pyc
│ │ │ │ │ │ │ └── python.cpython-312.pyc
│ │ │ │ │ │ ├── _mapping.py
│ │ │ │ │ │ └── python.py
│ │ │ │ │ ├── modeline.py
│ │ │ │ │ ├── plugin.py
│ │ │ │ │ ├── regexopt.py
│ │ │ │ │ ├── scanner.py
│ │ │ │ │ ├── sphinxext.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styles
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── _mapping.cpython-312.pyc
│ │ │ │ │ │ └── _mapping.py
│ │ │ │ │ ├── token.py
│ │ │ │ │ ├── unistring.py
│ │ │ │ │ └── util.py
│ │ │ │ ├── pyproject_hooks
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _compat.cpython-312.pyc
│ │ │ │ │ │ └── _impl.cpython-312.pyc
│ │ │ │ │ ├── _compat.py
│ │ │ │ │ ├── _impl.py
│ │ │ │ │ └── _in_process
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ └── _in_process.cpython-312.pyc
│ │ │ │ │ └── _in_process.py
│ │ │ │ ├── requests
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ │ ├── __version__.py
│ │ │ │ │ ├── _internal_utils.py
│ │ │ │ │ ├── adapters.py
│ │ │ │ │ ├── api.py
│ │ │ │ │ ├── auth.py
│ │ │ │ │ ├── certs.py
│ │ │ │ │ ├── compat.py
│ │ │ │ │ ├── cookies.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── help.py
│ │ │ │ │ ├── hooks.py
│ │ │ │ │ ├── models.py
│ │ │ │ │ ├── packages.py
│ │ │ │ │ ├── sessions.py
│ │ │ │ │ ├── status_codes.py
│ │ │ │ │ ├── structures.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── resolvelib
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── providers.cpython-312.pyc
│ │ │ │ │ │ ├── reporters.cpython-312.pyc
│ │ │ │ │ │ ├── resolvers.cpython-312.pyc
│ │ │ │ │ │ └── structs.cpython-312.pyc
│ │ │ │ │ ├── compat
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── collections_abc.cpython-312.pyc
│ │ │ │ │ │ └── collections_abc.py
│ │ │ │ │ ├── providers.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── reporters.py
│ │ │ │ │ ├── resolvers.py
│ │ │ │ │ └── structs.py
│ │ │ │ ├── rich
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __main__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── __main__.cpython-312.pyc
│ │ │ │ │ │ ├── _cell_widths.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_codes.cpython-312.pyc
│ │ │ │ │ │ ├── _emoji_replace.cpython-312.pyc
│ │ │ │ │ │ ├── _export_format.cpython-312.pyc
│ │ │ │ │ │ ├── _extension.cpython-312.pyc
│ │ │ │ │ │ ├── _fileno.cpython-312.pyc
│ │ │ │ │ │ ├── _inspect.cpython-312.pyc
│ │ │ │ │ │ ├── _log_render.cpython-312.pyc
│ │ │ │ │ │ ├── _loop.cpython-312.pyc
│ │ │ │ │ │ ├── _null_file.cpython-312.pyc
│ │ │ │ │ │ ├── _palettes.cpython-312.pyc
│ │ │ │ │ │ ├── _pick.cpython-312.pyc
│ │ │ │ │ │ ├── _ratio.cpython-312.pyc
│ │ │ │ │ │ ├── _spinners.cpython-312.pyc
│ │ │ │ │ │ ├── _stack.cpython-312.pyc
│ │ │ │ │ │ ├── _timer.cpython-312.pyc
│ │ │ │ │ │ ├── _win32_console.cpython-312.pyc
│ │ │ │ │ │ ├── _windows_renderer.cpython-312.pyc
│ │ │ │ │ │ ├── _windows.cpython-312.pyc
│ │ │ │ │ │ ├── _wrap.cpython-312.pyc
│ │ │ │ │ │ ├── abc.cpython-312.pyc
│ │ │ │ │ │ ├── align.cpython-312.pyc
│ │ │ │ │ │ ├── ansi.cpython-312.pyc
│ │ │ │ │ │ ├── bar.cpython-312.pyc
│ │ │ │ │ │ ├── box.cpython-312.pyc
│ │ │ │ │ │ ├── cells.cpython-312.pyc
│ │ │ │ │ │ ├── color_triplet.cpython-312.pyc
│ │ │ │ │ │ ├── color.cpython-312.pyc
│ │ │ │ │ │ ├── columns.cpython-312.pyc
│ │ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ │ ├── constrain.cpython-312.pyc
│ │ │ │ │ │ ├── containers.cpython-312.pyc
│ │ │ │ │ │ ├── control.cpython-312.pyc
│ │ │ │ │ │ ├── default_styles.cpython-312.pyc
│ │ │ │ │ │ ├── diagnose.cpython-312.pyc
│ │ │ │ │ │ ├── emoji.cpython-312.pyc
│ │ │ │ │ │ ├── errors.cpython-312.pyc
│ │ │ │ │ │ ├── file_proxy.cpython-312.pyc
│ │ │ │ │ │ ├── filesize.cpython-312.pyc
│ │ │ │ │ │ ├── highlighter.cpython-312.pyc
│ │ │ │ │ │ ├── json.cpython-312.pyc
│ │ │ │ │ │ ├── jupyter.cpython-312.pyc
│ │ │ │ │ │ ├── layout.cpython-312.pyc
│ │ │ │ │ │ ├── live_render.cpython-312.pyc
│ │ │ │ │ │ ├── live.cpython-312.pyc
│ │ │ │ │ │ ├── logging.cpython-312.pyc
│ │ │ │ │ │ ├── markup.cpython-312.pyc
│ │ │ │ │ │ ├── measure.cpython-312.pyc
│ │ │ │ │ │ ├── padding.cpython-312.pyc
│ │ │ │ │ │ ├── pager.cpython-312.pyc
│ │ │ │ │ │ ├── palette.cpython-312.pyc
│ │ │ │ │ │ ├── panel.cpython-312.pyc
│ │ │ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ │ │ ├── progress_bar.cpython-312.pyc
│ │ │ │ │ │ ├── progress.cpython-312.pyc
│ │ │ │ │ │ ├── prompt.cpython-312.pyc
│ │ │ │ │ │ ├── protocol.cpython-312.pyc
│ │ │ │ │ │ ├── region.cpython-312.pyc
│ │ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ │ ├── rule.cpython-312.pyc
│ │ │ │ │ │ ├── scope.cpython-312.pyc
│ │ │ │ │ │ ├── screen.cpython-312.pyc
│ │ │ │ │ │ ├── segment.cpython-312.pyc
│ │ │ │ │ │ ├── spinner.cpython-312.pyc
│ │ │ │ │ │ ├── status.cpython-312.pyc
│ │ │ │ │ │ ├── style.cpython-312.pyc
│ │ │ │ │ │ ├── styled.cpython-312.pyc
│ │ │ │ │ │ ├── syntax.cpython-312.pyc
│ │ │ │ │ │ ├── table.cpython-312.pyc
│ │ │ │ │ │ ├── terminal_theme.cpython-312.pyc
│ │ │ │ │ │ ├── text.cpython-312.pyc
│ │ │ │ │ │ ├── theme.cpython-312.pyc
│ │ │ │ │ │ ├── themes.cpython-312.pyc
│ │ │ │ │ │ ├── traceback.cpython-312.pyc
│ │ │ │ │ │ └── tree.cpython-312.pyc
│ │ │ │ │ ├── _cell_widths.py
│ │ │ │ │ ├── _emoji_codes.py
│ │ │ │ │ ├── _emoji_replace.py
│ │ │ │ │ ├── _export_format.py
│ │ │ │ │ ├── _extension.py
│ │ │ │ │ ├── _fileno.py
│ │ │ │ │ ├── _inspect.py
│ │ │ │ │ ├── _log_render.py
│ │ │ │ │ ├── _loop.py
│ │ │ │ │ ├── _null_file.py
│ │ │ │ │ ├── _palettes.py
│ │ │ │ │ ├── _pick.py
│ │ │ │ │ ├── _ratio.py
│ │ │ │ │ ├── _spinners.py
│ │ │ │ │ ├── _stack.py
│ │ │ │ │ ├── _timer.py
│ │ │ │ │ ├── _win32_console.py
│ │ │ │ │ ├── _windows_renderer.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ ├── _wrap.py
│ │ │ │ │ ├── abc.py
│ │ │ │ │ ├── align.py
│ │ │ │ │ ├── ansi.py
│ │ │ │ │ ├── bar.py
│ │ │ │ │ ├── box.py
│ │ │ │ │ ├── cells.py
│ │ │ │ │ ├── color_triplet.py
│ │ │ │ │ ├── color.py
│ │ │ │ │ ├── columns.py
│ │ │ │ │ ├── console.py
│ │ │ │ │ ├── constrain.py
│ │ │ │ │ ├── containers.py
│ │ │ │ │ ├── control.py
│ │ │ │ │ ├── default_styles.py
│ │ │ │ │ ├── diagnose.py
│ │ │ │ │ ├── emoji.py
│ │ │ │ │ ├── errors.py
│ │ │ │ │ ├── file_proxy.py
│ │ │ │ │ ├── filesize.py
│ │ │ │ │ ├── highlighter.py
│ │ │ │ │ ├── json.py
│ │ │ │ │ ├── jupyter.py
│ │ │ │ │ ├── layout.py
│ │ │ │ │ ├── live_render.py
│ │ │ │ │ ├── live.py
│ │ │ │ │ ├── logging.py
│ │ │ │ │ ├── markup.py
│ │ │ │ │ ├── measure.py
│ │ │ │ │ ├── padding.py
│ │ │ │ │ ├── pager.py
│ │ │ │ │ ├── palette.py
│ │ │ │ │ ├── panel.py
│ │ │ │ │ ├── pretty.py
│ │ │ │ │ ├── progress_bar.py
│ │ │ │ │ ├── progress.py
│ │ │ │ │ ├── prompt.py
│ │ │ │ │ ├── protocol.py
│ │ │ │ │ ├── py.typed
│ │ │ │ │ ├── region.py
│ │ │ │ │ ├── repr.py
│ │ │ │ │ ├── rule.py
│ │ │ │ │ ├── scope.py
│ │ │ │ │ ├── screen.py
│ │ │ │ │ ├── segment.py
│ │ │ │ │ ├── spinner.py
│ │ │ │ │ ├── status.py
│ │ │ │ │ ├── style.py
│ │ │ │ │ ├── styled.py
│ │ │ │ │ ├── syntax.py
│ │ │ │ │ ├── table.py
│ │ │ │ │ ├── terminal_theme.py
│ │ │ │ │ ├── text.py
│ │ │ │ │ ├── theme.py
│ │ │ │ │ ├── themes.py
│ │ │ │ │ ├── traceback.py
│ │ │ │ │ └── tree.py
│ │ │ │ ├── tomli
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _parser.cpython-312.pyc
│ │ │ │ │ │ ├── _re.cpython-312.pyc
│ │ │ │ │ │ └── _types.cpython-312.pyc
│ │ │ │ │ ├── _parser.py
│ │ │ │ │ ├── _re.py
│ │ │ │ │ ├── _types.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── truststore
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _api.cpython-312.pyc
│ │ │ │ │ │ ├── _macos.cpython-312.pyc
│ │ │ │ │ │ ├── _openssl.cpython-312.pyc
│ │ │ │ │ │ ├── _ssl_constants.cpython-312.pyc
│ │ │ │ │ │ └── _windows.cpython-312.pyc
│ │ │ │ │ ├── _api.py
│ │ │ │ │ ├── _macos.py
│ │ │ │ │ ├── _openssl.py
│ │ │ │ │ ├── _ssl_constants.py
│ │ │ │ │ ├── _windows.py
│ │ │ │ │ └── py.typed
│ │ │ │ ├── typing_extensions.py
│ │ │ │ ├── urllib3
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── _collections.py
│ │ │ │ │ ├── _version.py
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── connectionpool.py
│ │ │ │ │ ├── contrib
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ ├── _appengine_environ.cpython-312.pyc
│ │ │ │ │ │ │ ├── appengine.cpython-312.pyc
│ │ │ │ │ │ │ ├── ntlmpool.cpython-312.pyc
│ │ │ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ │ │ ├── securetransport.cpython-312.pyc
│ │ │ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ │ │ ├── _appengine_environ.py
│ │ │ │ │ │ ├── _securetransport
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── bindings.cpython-312.pyc
│ │ │ │ │ │ │ │ └── low_level.cpython-312.pyc
│ │ │ │ │ │ │ ├── bindings.py
│ │ │ │ │ │ │ └── low_level.py
│ │ │ │ │ │ ├── appengine.py
│ │ │ │ │ │ ├── ntlmpool.py
│ │ │ │ │ │ ├── pyopenssl.py
│ │ │ │ │ │ ├── securetransport.py
│ │ │ │ │ │ └── socks.py
│ │ │ │ │ ├── exceptions.py
│ │ │ │ │ ├── fields.py
│ │ │ │ │ ├── filepost.py
│ │ │ │ │ ├── packages
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ └── six.cpython-312.pyc
│ │ │ │ │ │ ├── backports
│ │ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ │ │ ├── makefile.cpython-312.pyc
│ │ │ │ │ │ │ │ └── weakref_finalize.cpython-312.pyc
│ │ │ │ │ │ │ ├── makefile.py
│ │ │ │ │ │ │ └── weakref_finalize.py
│ │ │ │ │ │ └── six.py
│ │ │ │ │ ├── poolmanager.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ └── util
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ │ │ ├── queue.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ │ │ └── wait.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── proxy.py
│ │ │ │ │ ├── queue.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ ├── response.py
│ │ │ │ │ ├── retry.py
│ │ │ │ │ ├── ssl_.py
│ │ │ │ │ ├── ssl_match_hostname.py
│ │ │ │ │ ├── ssltransport.py
│ │ │ │ │ ├── timeout.py
│ │ │ │ │ ├── url.py
│ │ │ │ │ └── wait.py
│ │ │ │ └── vendor.txt
│ │ │ └── py.typed
│ │ ├── pip-24.2.dist-info
│ │ │ ├── AUTHORS.txt
│ │ │ ├── entry_points.txt
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── requests
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __version__.cpython-312.pyc
│ │ │ │ ├── _internal_utils.cpython-312.pyc
│ │ │ │ ├── adapters.cpython-312.pyc
│ │ │ │ ├── api.cpython-312.pyc
│ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ ├── certs.cpython-312.pyc
│ │ │ │ ├── compat.cpython-312.pyc
│ │ │ │ ├── cookies.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── help.cpython-312.pyc
│ │ │ │ ├── hooks.cpython-312.pyc
│ │ │ │ ├── models.cpython-312.pyc
│ │ │ │ ├── packages.cpython-312.pyc
│ │ │ │ ├── sessions.cpython-312.pyc
│ │ │ │ ├── status_codes.cpython-312.pyc
│ │ │ │ ├── structures.cpython-312.pyc
│ │ │ │ └── utils.cpython-312.pyc
│ │ │ ├── __version__.py
│ │ │ ├── _internal_utils.py
│ │ │ ├── adapters.py
│ │ │ ├── api.py
│ │ │ ├── auth.py
│ │ │ ├── certs.py
│ │ │ ├── compat.py
│ │ │ ├── cookies.py
│ │ │ ├── exceptions.py
│ │ │ ├── help.py
│ │ │ ├── hooks.py
│ │ │ ├── models.py
│ │ │ ├── packages.py
│ │ │ ├── sessions.py
│ │ │ ├── status_codes.py
│ │ │ ├── structures.py
│ │ │ └── utils.py
│ │ ├── requests-2.32.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── soupsieve
│ │ │ ├── __init__.py
│ │ │ ├── __meta__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── __meta__.cpython-312.pyc
│ │ │ │ ├── css_match.cpython-312.pyc
│ │ │ │ ├── css_parser.cpython-312.pyc
│ │ │ │ ├── css_types.cpython-312.pyc
│ │ │ │ ├── pretty.cpython-312.pyc
│ │ │ │ └── util.cpython-312.pyc
│ │ │ ├── css_match.py
│ │ │ ├── css_parser.py
│ │ │ ├── css_types.py
│ │ │ ├── pretty.py
│ │ │ ├── py.typed
│ │ │ └── util.py
│ │ ├── soupsieve-2.6.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.md
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── urllib3
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _base_connection.cpython-312.pyc
│ │ │ │ ├── _collections.cpython-312.pyc
│ │ │ │ ├── _request_methods.cpython-312.pyc
│ │ │ │ ├── _version.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── connectionpool.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── fields.cpython-312.pyc
│ │ │ │ ├── filepost.cpython-312.pyc
│ │ │ │ ├── poolmanager.cpython-312.pyc
│ │ │ │ └── response.cpython-312.pyc
│ │ │ ├── _base_connection.py
│ │ │ ├── _collections.py
│ │ │ ├── _request_methods.py
│ │ │ ├── _version.py
│ │ │ ├── connection.py
│ │ │ ├── connectionpool.py
│ │ │ ├── contrib
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── pyopenssl.cpython-312.pyc
│ │ │ │ │ └── socks.cpython-312.pyc
│ │ │ │ ├── emscripten
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── __pycache__
│ │ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ │ ├── fetch.cpython-312.pyc
│ │ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ │ ├── connection.py
│ │ │ │ │ ├── emscripten_fetch_worker.js
│ │ │ │ │ ├── fetch.py
│ │ │ │ │ ├── request.py
│ │ │ │ │ └── response.py
│ │ │ │ ├── pyopenssl.py
│ │ │ │ └── socks.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── filepost.py
│ │ │ ├── http2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ │ └── probe.cpython-312.pyc
│ │ │ │ ├── connection.py
│ │ │ │ └── probe.py
│ │ │ ├── poolmanager.py
│ │ │ ├── py.typed
│ │ │ ├── response.py
│ │ │ └── util
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── connection.cpython-312.pyc
│ │ │ │ ├── proxy.cpython-312.pyc
│ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ ├── retry.cpython-312.pyc
│ │ │ │ ├── ssl_.cpython-312.pyc
│ │ │ │ ├── ssl_match_hostname.cpython-312.pyc
│ │ │ │ ├── ssltransport.cpython-312.pyc
│ │ │ │ ├── timeout.cpython-312.pyc
│ │ │ │ ├── url.cpython-312.pyc
│ │ │ │ ├── util.cpython-312.pyc
│ │ │ │ └── wait.cpython-312.pyc
│ │ │ ├── connection.py
│ │ │ ├── proxy.py
│ │ │ ├── request.py
│ │ │ ├── response.py
│ │ │ ├── retry.py
│ │ │ ├── ssl_.py
│ │ │ ├── ssl_match_hostname.py
│ │ │ ├── ssltransport.py
│ │ │ ├── timeout.py
│ │ │ ├── url.py
│ │ │ ├── util.py
│ │ │ └── wait.py
│ │ ├── urllib3-2.2.3.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── licenses
│ │ │ │ └── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ └── WHEEL
│ │ ├── useragent
│ │ │ ├── __init__.py
│ │ │ ├── __init__.pyc
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── resources
│ │ │ │ └── user_agent_data.json
│ │ │ └── test
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ └── __init__.cpython-312.pyc
│ │ │ ├── test_additional_os.json
│ │ │ ├── test_browser.json
│ │ │ ├── test_device.json
│ │ │ ├── test_firefox.json
│ │ │ ├── test_os.json
│ │ │ └── test_pgts_browser.json
│ │ ├── useragent-0.1.1.dist-info
│ │ │ ├── INSTALLER
│ │ │ ├── LICENSE.txt
│ │ │ ├── METADATA
│ │ │ ├── RECORD
│ │ │ ├── REQUESTED
│ │ │ ├── top_level.txt
│ │ │ └── WHEEL
│ │ ├── werkzeug
│ │ │ ├── __init__.py
│ │ │ ├── __pycache__
│ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ ├── _internal.cpython-312.pyc
│ │ │ │ ├── _reloader.cpython-312.pyc
│ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ ├── formparser.cpython-312.pyc
│ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ ├── local.cpython-312.pyc
│ │ │ │ ├── security.cpython-312.pyc
│ │ │ │ ├── serving.cpython-312.pyc
│ │ │ │ ├── test.cpython-312.pyc
│ │ │ │ ├── testapp.cpython-312.pyc
│ │ │ │ ├── urls.cpython-312.pyc
│ │ │ │ ├── user_agent.cpython-312.pyc
│ │ │ │ ├── utils.cpython-312.pyc
│ │ │ │ └── wsgi.cpython-312.pyc
│ │ │ ├── _internal.py
│ │ │ ├── _reloader.py
│ │ │ ├── datastructures
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── accept.cpython-312.pyc
│ │ │ │ │ ├── auth.cpython-312.pyc
│ │ │ │ │ ├── cache_control.cpython-312.pyc
│ │ │ │ │ ├── csp.cpython-312.pyc
│ │ │ │ │ ├── etag.cpython-312.pyc
│ │ │ │ │ ├── file_storage.cpython-312.pyc
│ │ │ │ │ ├── headers.cpython-312.pyc
│ │ │ │ │ ├── mixins.cpython-312.pyc
│ │ │ │ │ ├── range.cpython-312.pyc
│ │ │ │ │ └── structures.cpython-312.pyc
│ │ │ │ ├── accept.py
│ │ │ │ ├── accept.pyi
│ │ │ │ ├── auth.py
│ │ │ │ ├── cache_control.py
│ │ │ │ ├── cache_control.pyi
│ │ │ │ ├── csp.py
│ │ │ │ ├── csp.pyi
│ │ │ │ ├── etag.py
│ │ │ │ ├── etag.pyi
│ │ │ │ ├── file_storage.py
│ │ │ │ ├── file_storage.pyi
│ │ │ │ ├── headers.py
│ │ │ │ ├── headers.pyi
│ │ │ │ ├── mixins.py
│ │ │ │ ├── mixins.pyi
│ │ │ │ ├── range.py
│ │ │ │ ├── range.pyi
│ │ │ │ ├── structures.py
│ │ │ │ └── structures.pyi
│ │ │ ├── debug
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── console.cpython-312.pyc
│ │ │ │ │ ├── repr.cpython-312.pyc
│ │ │ │ │ └── tbtools.cpython-312.pyc
│ │ │ │ ├── console.py
│ │ │ │ ├── repr.py
│ │ │ │ ├── shared
│ │ │ │ │ ├── console.png
│ │ │ │ │ ├── debugger.js
│ │ │ │ │ ├── ICON_LICENSE.md
│ │ │ │ │ ├── less.png
│ │ │ │ │ ├── more.png
│ │ │ │ │ └── style.css
│ │ │ │ └── tbtools.py
│ │ │ ├── exceptions.py
│ │ │ ├── formparser.py
│ │ │ ├── http.py
│ │ │ ├── local.py
│ │ │ ├── middleware
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── dispatcher.cpython-312.pyc
│ │ │ │ │ ├── http_proxy.cpython-312.pyc
│ │ │ │ │ ├── lint.cpython-312.pyc
│ │ │ │ │ ├── profiler.cpython-312.pyc
│ │ │ │ │ ├── proxy_fix.cpython-312.pyc
│ │ │ │ │ └── shared_data.cpython-312.pyc
│ │ │ │ ├── dispatcher.py
│ │ │ │ ├── http_proxy.py
│ │ │ │ ├── lint.py
│ │ │ │ ├── profiler.py
│ │ │ │ ├── proxy_fix.py
│ │ │ │ └── shared_data.py
│ │ │ ├── py.typed
│ │ │ ├── routing
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── converters.cpython-312.pyc
│ │ │ │ │ ├── exceptions.cpython-312.pyc
│ │ │ │ │ ├── map.cpython-312.pyc
│ │ │ │ │ ├── matcher.cpython-312.pyc
│ │ │ │ │ └── rules.cpython-312.pyc
│ │ │ │ ├── converters.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── map.py
│ │ │ │ ├── matcher.py
│ │ │ │ └── rules.py
│ │ │ ├── sansio
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── http.cpython-312.pyc
│ │ │ │ │ ├── multipart.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ ├── response.cpython-312.pyc
│ │ │ │ │ └── utils.cpython-312.pyc
│ │ │ │ ├── http.py
│ │ │ │ ├── multipart.py
│ │ │ │ ├── request.py
│ │ │ │ ├── response.py
│ │ │ │ └── utils.py
│ │ │ ├── security.py
│ │ │ ├── serving.py
│ │ │ ├── test.py
│ │ │ ├── testapp.py
│ │ │ ├── urls.py
│ │ │ ├── user_agent.py
│ │ │ ├── utils.py
│ │ │ ├── wrappers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── __pycache__
│ │ │ │ │ ├── __init__.cpython-312.pyc
│ │ │ │ │ ├── request.cpython-312.pyc
│ │ │ │ │ └── response.cpython-312.pyc
│ │ │ │ ├── request.py
│ │ │ │ └── response.py
│ │ │ └── wsgi.py
│ │ └── werkzeug-3.0.4.dist-info
│ │ ├── INSTALLER
│ │ ├── LICENSE.txt
│ │ ├── METADATA
│ │ ├── RECORD
│ │ └── WHEEL
│ ├── pyvenv.cfg
│ ├── static
│ │ └── styles.css
│ ├── templates
│ │ └── index.html
│ └── test.py
├── cline_config.json
├── mcp_server.py
├── README.md
├── search_results.json
├── settings.json
└── test_files
├── text1.txt
└── text2.txt
```
# Files
--------------------------------------------------------------------------------
/.venv/lib/python3.12/site-packages/bs4/tests/__init__.py:
--------------------------------------------------------------------------------
```python
1 | # encoding: utf-8
2 | """Helper classes for tests."""
3 |
4 | # Use of this source code is governed by the MIT license.
5 | __license__ = "MIT"
6 |
7 | import pickle
8 | import copy
9 | import functools
10 | import warnings
11 | import pytest
12 | from bs4 import BeautifulSoup
13 | from bs4.element import (
14 | CharsetMetaAttributeValue,
15 | Comment,
16 | ContentMetaAttributeValue,
17 | Doctype,
18 | PYTHON_SPECIFIC_ENCODINGS,
19 | SoupStrainer,
20 | Script,
21 | Stylesheet,
22 | Tag
23 | )
24 |
25 | from bs4.builder import (
26 | DetectsXMLParsedAsHTML,
27 | HTMLParserTreeBuilder,
28 | XMLParsedAsHTMLWarning,
29 | )
30 | default_builder = HTMLParserTreeBuilder
31 |
32 | # Some tests depend on specific third-party libraries. We use
33 | # @pytest.mark.skipIf on the following conditionals to skip them
34 | # if the libraries are not installed.
35 | try:
36 | from soupsieve import SelectorSyntaxError
37 | SOUP_SIEVE_PRESENT = True
38 | except ImportError:
39 | SOUP_SIEVE_PRESENT = False
40 |
41 | try:
42 | import html5lib
43 | HTML5LIB_PRESENT = True
44 | except ImportError:
45 | HTML5LIB_PRESENT = False
46 |
47 | try:
48 | import lxml.etree
49 | LXML_PRESENT = True
50 | LXML_VERSION = lxml.etree.LXML_VERSION
51 | except ImportError:
52 | LXML_PRESENT = False
53 | LXML_VERSION = (0,)
54 |
55 | BAD_DOCUMENT = """A bare string
56 | <!DOCTYPE xsl:stylesheet SYSTEM "htmlent.dtd">
57 | <!DOCTYPE xsl:stylesheet PUBLIC "htmlent.dtd">
58 | <div><![CDATA[A CDATA section where it doesn't belong]]></div>
59 | <div><svg><![CDATA[HTML5 does allow CDATA sections in SVG]]></svg></div>
60 | <div>A <meta> tag</div>
61 | <div>A <br> tag that supposedly has contents.</br></div>
62 | <div>AT&T</div>
63 | <div><textarea>Within a textarea, markup like <b> tags and <&<& should be treated as literal</textarea></div>
64 | <div><script>if (i < 2) { alert("<b>Markup within script tags should be treated as literal.</b>"); }</script></div>
65 | <div>This numeric entity is missing the final semicolon: <x t="piñata"></div>
66 | <div><a href="http://example.com/</a> that attribute value never got closed</div>
67 | <div><a href="foo</a>, </a><a href="bar">that attribute value was closed by the subsequent tag</a></div>
68 | <! This document starts with a bogus declaration ><div>a</div>
69 | <div>This document contains <!an incomplete declaration <div>(do you see it?)</div>
70 | <div>This document ends with <!an incomplete declaration
71 | <div><a style={height:21px;}>That attribute value was bogus</a></div>
72 | <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">The doctype is invalid because it contains extra whitespace
73 | <div><table><td nowrap>That boolean attribute had no value</td></table></div>
74 | <div>Here's a nonexistent entity: &#foo; (do you see it?)</div>
75 | <div>This document ends before the entity finishes: >
76 | <div><p>Paragraphs shouldn't contain block display elements, but this one does: <dl><dt>you see?</dt></p>
77 | <b b="20" a="1" b="10" a="2" a="3" a="4">Multiple values for the same attribute.</b>
78 | <div><table><tr><td>Here's a table</td></tr></table></div>
79 | <div><table id="1"><tr><td>Here's a nested table:<table id="2"><tr><td>foo</td></tr></table></td></div>
80 | <div>This tag contains nothing but whitespace: <b> </b></div>
81 | <div><blockquote><p><b>This p tag is cut off by</blockquote></p>the end of the blockquote tag</div>
82 | <div><table><div>This table contains bare markup</div></table></div>
83 | <div><div id="1">\n <a href="link1">This link is never closed.\n</div>\n<div id="2">\n <div id="3">\n <a href="link2">This link is closed.</a>\n </div>\n</div></div>
84 | <div>This document contains a <!DOCTYPE surprise>surprise doctype</div>
85 | <div><a><B><Cd><EFG>Mixed case tags are folded to lowercase</efg></CD></b></A></div>
86 | <div><our\u2603>Tag name contains Unicode characters</our\u2603></div>
87 | <div><a \u2603="snowman">Attribute name contains Unicode characters</a></div>
88 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
89 | """
90 |
91 |
92 | class SoupTest(object):
93 |
94 | @property
95 | def default_builder(self):
96 | return default_builder
97 |
98 | def soup(self, markup, **kwargs):
99 | """Build a Beautiful Soup object from markup."""
100 | builder = kwargs.pop('builder', self.default_builder)
101 | return BeautifulSoup(markup, builder=builder, **kwargs)
102 |
103 | def document_for(self, markup, **kwargs):
104 | """Turn an HTML fragment into a document.
105 |
106 | The details depend on the builder.
107 | """
108 | return self.default_builder(**kwargs).test_fragment_to_document(markup)
109 |
110 | def assert_soup(self, to_parse, compare_parsed_to=None):
111 | """Parse some markup using Beautiful Soup and verify that
112 | the output markup is as expected.
113 | """
114 | builder = self.default_builder
115 | obj = BeautifulSoup(to_parse, builder=builder)
116 | if compare_parsed_to is None:
117 | compare_parsed_to = to_parse
118 |
119 | # Verify that the documents come out the same.
120 | assert obj.decode() == self.document_for(compare_parsed_to)
121 |
122 | # Also run some checks on the BeautifulSoup object itself:
123 |
124 | # Verify that every tag that was opened was eventually closed.
125 |
126 | # There are no tags in the open tag counter.
127 | assert all(v==0 for v in list(obj.open_tag_counter.values()))
128 |
129 | # The only tag in the tag stack is the one for the root
130 | # document.
131 | assert [obj.ROOT_TAG_NAME] == [x.name for x in obj.tagStack]
132 |
133 | assertSoupEquals = assert_soup
134 |
135 | def assertConnectedness(self, element):
136 | """Ensure that next_element and previous_element are properly
137 | set for all descendants of the given element.
138 | """
139 | earlier = None
140 | for e in element.descendants:
141 | if earlier:
142 | assert e == earlier.next_element
143 | assert earlier == e.previous_element
144 | earlier = e
145 |
146 | def linkage_validator(self, el, _recursive_call=False):
147 | """Ensure proper linkage throughout the document."""
148 | descendant = None
149 | # Document element should have no previous element or previous sibling.
150 | # It also shouldn't have a next sibling.
151 | if el.parent is None:
152 | assert el.previous_element is None,\
153 | "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format(
154 | el, el.previous_element, None
155 | )
156 | assert el.previous_sibling is None,\
157 | "Bad previous_sibling\nNODE: {}\nPREV: {}\nEXPECTED: {}".format(
158 | el, el.previous_sibling, None
159 | )
160 | assert el.next_sibling is None,\
161 | "Bad next_sibling\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format(
162 | el, el.next_sibling, None
163 | )
164 |
165 | idx = 0
166 | child = None
167 | last_child = None
168 | last_idx = len(el.contents) - 1
169 | for child in el.contents:
170 | descendant = None
171 |
172 | # Parent should link next element to their first child
173 | # That child should have no previous sibling
174 | if idx == 0:
175 | if el.parent is not None:
176 | assert el.next_element is child,\
177 | "Bad next_element\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format(
178 | el, el.next_element, child
179 | )
180 | assert child.previous_element is el,\
181 | "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format(
182 | child, child.previous_element, el
183 | )
184 | assert child.previous_sibling is None,\
185 | "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED: {}".format(
186 | child, child.previous_sibling, None
187 | )
188 |
189 | # If not the first child, previous index should link as sibling to this index
190 | # Previous element should match the last index or the last bubbled up descendant
191 | else:
192 | assert child.previous_sibling is el.contents[idx - 1],\
193 | "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED {}".format(
194 | child, child.previous_sibling, el.contents[idx - 1]
195 | )
196 | assert el.contents[idx - 1].next_sibling is child,\
197 | "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
198 | el.contents[idx - 1], el.contents[idx - 1].next_sibling, child
199 | )
200 |
201 | if last_child is not None:
202 | assert child.previous_element is last_child,\
203 | "Bad previous_element\nNODE: {}\nPREV {}\nEXPECTED {}\nCONTENTS {}".format(
204 | child, child.previous_element, last_child, child.parent.contents
205 | )
206 | assert last_child.next_element is child,\
207 | "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
208 | last_child, last_child.next_element, child
209 | )
210 |
211 | if isinstance(child, Tag) and child.contents:
212 | descendant = self.linkage_validator(child, True)
213 | # A bubbled up descendant should have no next siblings
214 | assert descendant.next_sibling is None,\
215 | "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
216 | descendant, descendant.next_sibling, None
217 | )
218 |
219 | # Mark last child as either the bubbled up descendant or the current child
220 | if descendant is not None:
221 | last_child = descendant
222 | else:
223 | last_child = child
224 |
225 | # If last child, there are non next siblings
226 | if idx == last_idx:
227 | assert child.next_sibling is None,\
228 | "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
229 | child, child.next_sibling, None
230 | )
231 | idx += 1
232 |
233 | child = descendant if descendant is not None else child
234 | if child is None:
235 | child = el
236 |
237 | if not _recursive_call and child is not None:
238 | target = el
239 | while True:
240 | if target is None:
241 | assert child.next_element is None, \
242 | "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
243 | child, child.next_element, None
244 | )
245 | break
246 | elif target.next_sibling is not None:
247 | assert child.next_element is target.next_sibling, \
248 | "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format(
249 | child, child.next_element, target.next_sibling
250 | )
251 | break
252 | target = target.parent
253 |
254 | # We are done, so nothing to return
255 | return None
256 | else:
257 | # Return the child to the recursive caller
258 | return child
259 |
260 | def assert_selects(self, tags, should_match):
261 | """Make sure that the given tags have the correct text.
262 |
263 | This is used in tests that define a bunch of tags, each
264 | containing a single string, and then select certain strings by
265 | some mechanism.
266 | """
267 | assert [tag.string for tag in tags] == should_match
268 |
269 | def assert_selects_ids(self, tags, should_match):
270 | """Make sure that the given tags have the correct IDs.
271 |
272 | This is used in tests that define a bunch of tags, each
273 | containing a single string, and then select certain strings by
274 | some mechanism.
275 | """
276 | assert [tag['id'] for tag in tags] == should_match
277 |
278 |
279 | class TreeBuilderSmokeTest(object):
280 | # Tests that are common to HTML and XML tree builders.
281 |
282 | @pytest.mark.parametrize(
283 | "multi_valued_attributes",
284 | [None, {}, dict(b=['class']), {'*': ['notclass']}]
285 | )
286 | def test_attribute_not_multi_valued(self, multi_valued_attributes):
287 | markup = '<html xmlns="http://www.w3.org/1999/xhtml"><a class="a b c"></html>'
288 | soup = self.soup(markup, multi_valued_attributes=multi_valued_attributes)
289 | assert soup.a['class'] == 'a b c'
290 |
291 | @pytest.mark.parametrize(
292 | "multi_valued_attributes", [dict(a=['class']), {'*': ['class']}]
293 | )
294 | def test_attribute_multi_valued(self, multi_valued_attributes):
295 | markup = '<a class="a b c">'
296 | soup = self.soup(
297 | markup, multi_valued_attributes=multi_valued_attributes
298 | )
299 | assert soup.a['class'] == ['a', 'b', 'c']
300 |
301 | def test_invalid_doctype(self):
302 | markup = '<![if word]>content<![endif]>'
303 | markup = '<!DOCTYPE html]ff>'
304 | soup = self.soup(markup)
305 |
306 | class HTMLTreeBuilderSmokeTest(TreeBuilderSmokeTest):
307 |
308 | """A basic test of a treebuilder's competence.
309 |
310 | Any HTML treebuilder, present or future, should be able to pass
311 | these tests. With invalid markup, there's room for interpretation,
312 | and different parsers can handle it differently. But with the
313 | markup in these tests, there's not much room for interpretation.
314 | """
315 |
316 | def test_empty_element_tags(self):
317 | """Verify that all HTML4 and HTML5 empty element (aka void element) tags
318 | are handled correctly.
319 | """
320 | for name in [
321 | 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
322 | 'spacer', 'frame'
323 | ]:
324 | soup = self.soup("")
325 | new_tag = soup.new_tag(name)
326 | assert new_tag.is_empty_element == True
327 |
328 | def test_special_string_containers(self):
329 | soup = self.soup(
330 | "<style>Some CSS</style><script>Some Javascript</script>"
331 | )
332 | assert isinstance(soup.style.string, Stylesheet)
333 | assert isinstance(soup.script.string, Script)
334 |
335 | soup = self.soup(
336 | "<style><!--Some CSS--></style>"
337 | )
338 | assert isinstance(soup.style.string, Stylesheet)
339 | # The contents of the style tag resemble an HTML comment, but
340 | # it's not treated as a comment.
341 | assert soup.style.string == "<!--Some CSS-->"
342 | assert isinstance(soup.style.string, Stylesheet)
343 |
344 | def test_pickle_and_unpickle_identity(self):
345 | # Pickling a tree, then unpickling it, yields a tree identical
346 | # to the original.
347 | tree = self.soup("<a><b>foo</a>")
348 | dumped = pickle.dumps(tree, 2)
349 | loaded = pickle.loads(dumped)
350 | assert loaded.__class__ == BeautifulSoup
351 | assert loaded.decode() == tree.decode()
352 |
353 | def assertDoctypeHandled(self, doctype_fragment):
354 | """Assert that a given doctype string is handled correctly."""
355 | doctype_str, soup = self._document_with_doctype(doctype_fragment)
356 |
357 | # Make sure a Doctype object was created.
358 | doctype = soup.contents[0]
359 | assert doctype.__class__ == Doctype
360 | assert doctype == doctype_fragment
361 | assert soup.encode("utf8")[:len(doctype_str)] == doctype_str
362 |
363 | # Make sure that the doctype was correctly associated with the
364 | # parse tree and that the rest of the document parsed.
365 | assert soup.p.contents[0] == 'foo'
366 |
367 | def _document_with_doctype(self, doctype_fragment, doctype_string="DOCTYPE"):
368 | """Generate and parse a document with the given doctype."""
369 | doctype = '<!%s %s>' % (doctype_string, doctype_fragment)
370 | markup = doctype + '\n<p>foo</p>'
371 | soup = self.soup(markup)
372 | return doctype.encode("utf8"), soup
373 |
374 | def test_normal_doctypes(self):
375 | """Make sure normal, everyday HTML doctypes are handled correctly."""
376 | self.assertDoctypeHandled("html")
377 | self.assertDoctypeHandled(
378 | 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"')
379 |
380 | def test_empty_doctype(self):
381 | soup = self.soup("<!DOCTYPE>")
382 | doctype = soup.contents[0]
383 | assert "" == doctype.strip()
384 |
385 | def test_mixed_case_doctype(self):
386 | # A lowercase or mixed-case doctype becomes a Doctype.
387 | for doctype_fragment in ("doctype", "DocType"):
388 | doctype_str, soup = self._document_with_doctype(
389 | "html", doctype_fragment
390 | )
391 |
392 | # Make sure a Doctype object was created and that the DOCTYPE
393 | # is uppercase.
394 | doctype = soup.contents[0]
395 | assert doctype.__class__ == Doctype
396 | assert doctype == "html"
397 | assert soup.encode("utf8")[:len(doctype_str)] == b"<!DOCTYPE html>"
398 |
399 | # Make sure that the doctype was correctly associated with the
400 | # parse tree and that the rest of the document parsed.
401 | assert soup.p.contents[0] == 'foo'
402 |
403 | def test_public_doctype_with_url(self):
404 | doctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"'
405 | self.assertDoctypeHandled(doctype)
406 |
407 | def test_system_doctype(self):
408 | self.assertDoctypeHandled('foo SYSTEM "http://www.example.com/"')
409 |
410 | def test_namespaced_system_doctype(self):
411 | # We can handle a namespaced doctype with a system ID.
412 | self.assertDoctypeHandled('xsl:stylesheet SYSTEM "htmlent.dtd"')
413 |
414 | def test_namespaced_public_doctype(self):
415 | # Test a namespaced doctype with a public id.
416 | self.assertDoctypeHandled('xsl:stylesheet PUBLIC "htmlent.dtd"')
417 |
418 | def test_real_xhtml_document(self):
419 | """A real XHTML document should come out more or less the same as it went in."""
420 | markup = b"""<?xml version="1.0" encoding="utf-8"?>
421 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
422 | <html xmlns="http://www.w3.org/1999/xhtml">
423 | <head><title>Hello.</title></head>
424 | <body>Goodbye.</body>
425 | </html>"""
426 | with warnings.catch_warnings(record=True) as w:
427 | soup = self.soup(markup)
428 | assert soup.encode("utf-8").replace(b"\n", b"") == markup.replace(b"\n", b"")
429 |
430 | # No warning was issued about parsing an XML document as HTML,
431 | # because XHTML is both.
432 | assert w == []
433 |
434 |
435 | def test_namespaced_html(self):
436 | # When a namespaced XML document is parsed as HTML it should
437 | # be treated as HTML with weird tag names.
438 | markup = b"""<ns1:foo>content</ns1:foo><ns1:foo/><ns2:foo/>"""
439 | with warnings.catch_warnings(record=True) as w:
440 | soup = self.soup(markup)
441 |
442 | assert 2 == len(soup.find_all("ns1:foo"))
443 |
444 | # n.b. no "you're parsing XML as HTML" warning was given
445 | # because there was no XML declaration.
446 | assert [] == w
447 |
448 | def test_detect_xml_parsed_as_html(self):
449 | # A warning is issued when parsing an XML document as HTML,
450 | # but basic stuff should still work.
451 | markup = b"""<?xml version="1.0" encoding="utf-8"?><tag>string</tag>"""
452 | with warnings.catch_warnings(record=True) as w:
453 | soup = self.soup(markup)
454 | assert soup.tag.string == 'string'
455 | [warning] = w
456 | assert isinstance(warning.message, XMLParsedAsHTMLWarning)
457 | assert str(warning.message) == XMLParsedAsHTMLWarning.MESSAGE
458 |
459 | # NOTE: the warning is not issued if the document appears to
460 | # be XHTML (tested with test_real_xhtml_document in the
461 | # superclass) or if there is no XML declaration (tested with
462 | # test_namespaced_html in the superclass).
463 |
464 | def test_processing_instruction(self):
465 | # We test both Unicode and bytestring to verify that
466 | # process_markup correctly sets processing_instruction_class
467 | # even when the markup is already Unicode and there is no
468 | # need to process anything.
469 | markup = """<?PITarget PIContent?>"""
470 | soup = self.soup(markup)
471 | assert markup == soup.decode()
472 |
473 | markup = b"""<?PITarget PIContent?>"""
474 | soup = self.soup(markup)
475 | assert markup == soup.encode("utf8")
476 |
477 | def test_deepcopy(self):
478 | """Make sure you can copy the tree builder.
479 |
480 | This is important because the builder is part of a
481 | BeautifulSoup object, and we want to be able to copy that.
482 | """
483 | copy.deepcopy(self.default_builder)
484 |
485 | def test_p_tag_is_never_empty_element(self):
486 | """A <p> tag is never designated as an empty-element tag.
487 |
488 | Even if the markup shows it as an empty-element tag, it
489 | shouldn't be presented that way.
490 | """
491 | soup = self.soup("<p/>")
492 | assert not soup.p.is_empty_element
493 | assert str(soup.p) == "<p></p>"
494 |
495 | def test_unclosed_tags_get_closed(self):
496 | """A tag that's not closed by the end of the document should be closed.
497 |
498 | This applies to all tags except empty-element tags.
499 | """
500 | self.assert_soup("<p>", "<p></p>")
501 | self.assert_soup("<b>", "<b></b>")
502 |
503 | self.assert_soup("<br>", "<br/>")
504 |
505 | def test_br_is_always_empty_element_tag(self):
506 | """A <br> tag is designated as an empty-element tag.
507 |
508 | Some parsers treat <br></br> as one <br/> tag, some parsers as
509 | two tags, but it should always be an empty-element tag.
510 | """
511 | soup = self.soup("<br></br>")
512 | assert soup.br.is_empty_element
513 | assert str(soup.br) == "<br/>"
514 |
515 | def test_nested_formatting_elements(self):
516 | self.assert_soup("<em><em></em></em>")
517 |
518 | def test_double_head(self):
519 | html = '''<!DOCTYPE html>
520 | <html>
521 | <head>
522 | <title>Ordinary HEAD element test</title>
523 | </head>
524 | <script type="text/javascript">
525 | alert("Help!");
526 | </script>
527 | <body>
528 | Hello, world!
529 | </body>
530 | </html>
531 | '''
532 | soup = self.soup(html)
533 | assert "text/javascript" == soup.find('script')['type']
534 |
535 | def test_comment(self):
536 | # Comments are represented as Comment objects.
537 | markup = "<p>foo<!--foobar-->baz</p>"
538 | self.assert_soup(markup)
539 |
540 | soup = self.soup(markup)
541 | comment = soup.find(string="foobar")
542 | assert comment.__class__ == Comment
543 |
544 | # The comment is properly integrated into the tree.
545 | foo = soup.find(string="foo")
546 | assert comment == foo.next_element
547 | baz = soup.find(string="baz")
548 | assert comment == baz.previous_element
549 |
550 | def test_preserved_whitespace_in_pre_and_textarea(self):
551 | """Whitespace must be preserved in <pre> and <textarea> tags,
552 | even if that would mean not prettifying the markup.
553 | """
554 | pre_markup = "<pre>a z</pre>\n"
555 | textarea_markup = "<textarea> woo\nwoo </textarea>\n"
556 | self.assert_soup(pre_markup)
557 | self.assert_soup(textarea_markup)
558 |
559 | soup = self.soup(pre_markup)
560 | assert soup.pre.prettify() == pre_markup
561 |
562 | soup = self.soup(textarea_markup)
563 | assert soup.textarea.prettify() == textarea_markup
564 |
565 | soup = self.soup("<textarea></textarea>")
566 | assert soup.textarea.prettify() == "<textarea></textarea>\n"
567 |
568 | def test_nested_inline_elements(self):
569 | """Inline elements can be nested indefinitely."""
570 | b_tag = "<b>Inside a B tag</b>"
571 | self.assert_soup(b_tag)
572 |
573 | nested_b_tag = "<p>A <i>nested <b>tag</b></i></p>"
574 | self.assert_soup(nested_b_tag)
575 |
576 | double_nested_b_tag = "<p>A <a>doubly <i>nested <b>tag</b></i></a></p>"
577 | self.assert_soup(nested_b_tag)
578 |
579 | def test_nested_block_level_elements(self):
580 | """Block elements can be nested."""
581 | soup = self.soup('<blockquote><p><b>Foo</b></p></blockquote>')
582 | blockquote = soup.blockquote
583 | assert blockquote.p.b.string == 'Foo'
584 | assert blockquote.b.string == 'Foo'
585 |
586 | def test_correctly_nested_tables(self):
587 | """One table can go inside another one."""
588 | markup = ('<table id="1">'
589 | '<tr>'
590 | "<td>Here's another table:"
591 | '<table id="2">'
592 | '<tr><td>foo</td></tr>'
593 | '</table></td>')
594 |
595 | self.assert_soup(
596 | markup,
597 | '<table id="1"><tr><td>Here\'s another table:'
598 | '<table id="2"><tr><td>foo</td></tr></table>'
599 | '</td></tr></table>')
600 |
601 | self.assert_soup(
602 | "<table><thead><tr><td>Foo</td></tr></thead>"
603 | "<tbody><tr><td>Bar</td></tr></tbody>"
604 | "<tfoot><tr><td>Baz</td></tr></tfoot></table>")
605 |
606 | def test_multivalued_attribute_with_whitespace(self):
607 | # Whitespace separating the values of a multi-valued attribute
608 | # should be ignored.
609 |
610 | markup = '<div class=" foo bar "></a>'
611 | soup = self.soup(markup)
612 | assert ['foo', 'bar'] == soup.div['class']
613 |
614 | # If you search by the literal name of the class it's like the whitespace
615 | # wasn't there.
616 | assert soup.div == soup.find('div', class_="foo bar")
617 |
618 | def test_deeply_nested_multivalued_attribute(self):
619 | # html5lib can set the attributes of the same tag many times
620 | # as it rearranges the tree. This has caused problems with
621 | # multivalued attributes.
622 | markup = '<table><div><div class="css"></div></div></table>'
623 | soup = self.soup(markup)
624 | assert ["css"] == soup.div.div['class']
625 |
626 | def test_multivalued_attribute_on_html(self):
627 | # html5lib uses a different API to set the attributes ot the
628 | # <html> tag. This has caused problems with multivalued
629 | # attributes.
630 | markup = '<html class="a b"></html>'
631 | soup = self.soup(markup)
632 | assert ["a", "b"] == soup.html['class']
633 |
634 | def test_angle_brackets_in_attribute_values_are_escaped(self):
635 | self.assert_soup('<a b="<a>"></a>', '<a b="<a>"></a>')
636 |
637 | def test_strings_resembling_character_entity_references(self):
638 | # "&T" and "&p" look like incomplete character entities, but they are
639 | # not.
640 | self.assert_soup(
641 | "<p>• AT&T is in the s&p 500</p>",
642 | "<p>\u2022 AT&T is in the s&p 500</p>"
643 | )
644 |
645 | def test_apos_entity(self):
646 | self.assert_soup(
647 | "<p>Bob's Bar</p>",
648 | "<p>Bob's Bar</p>",
649 | )
650 |
651 | def test_entities_in_foreign_document_encoding(self):
652 | # “ and ” are invalid numeric entities referencing
653 | # Windows-1252 characters. - references a character common
654 | # to Windows-1252 and Unicode, and ☃ references a
655 | # character only found in Unicode.
656 | #
657 | # All of these entities should be converted to Unicode
658 | # characters.
659 | markup = "<p>“Hello” -☃</p>"
660 | soup = self.soup(markup)
661 | assert "“Hello” -☃" == soup.p.string
662 |
663 | def test_entities_in_attributes_converted_to_unicode(self):
664 | expect = '<p id="pi\N{LATIN SMALL LETTER N WITH TILDE}ata"></p>'
665 | self.assert_soup('<p id="piñata"></p>', expect)
666 | self.assert_soup('<p id="piñata"></p>', expect)
667 | self.assert_soup('<p id="piñata"></p>', expect)
668 | self.assert_soup('<p id="piñata"></p>', expect)
669 |
670 | def test_entities_in_text_converted_to_unicode(self):
671 | expect = '<p>pi\N{LATIN SMALL LETTER N WITH TILDE}ata</p>'
672 | self.assert_soup("<p>piñata</p>", expect)
673 | self.assert_soup("<p>piñata</p>", expect)
674 | self.assert_soup("<p>piñata</p>", expect)
675 | self.assert_soup("<p>piñata</p>", expect)
676 |
677 | def test_quot_entity_converted_to_quotation_mark(self):
678 | self.assert_soup("<p>I said "good day!"</p>",
679 | '<p>I said "good day!"</p>')
680 |
681 | def test_out_of_range_entity(self):
682 | expect = "\N{REPLACEMENT CHARACTER}"
683 | self.assert_soup("�", expect)
684 | self.assert_soup("�", expect)
685 | self.assert_soup("�", expect)
686 |
687 | def test_multipart_strings(self):
688 | "Mostly to prevent a recurrence of a bug in the html5lib treebuilder."
689 | soup = self.soup("<html><h2>\nfoo</h2><p></p></html>")
690 | assert "p" == soup.h2.string.next_element.name
691 | assert "p" == soup.p.name
692 | self.assertConnectedness(soup)
693 |
694 | def test_empty_element_tags(self):
695 | """Verify consistent handling of empty-element tags,
696 | no matter how they come in through the markup.
697 | """
698 | self.assert_soup('<br/><br/><br/>', "<br/><br/><br/>")
699 | self.assert_soup('<br /><br /><br />', "<br/><br/><br/>")
700 |
701 | def test_head_tag_between_head_and_body(self):
702 | "Prevent recurrence of a bug in the html5lib treebuilder."
703 | content = """<html><head></head>
704 | <link></link>
705 | <body>foo</body>
706 | </html>
707 | """
708 | soup = self.soup(content)
709 | assert soup.html.body is not None
710 | self.assertConnectedness(soup)
711 |
712 | def test_multiple_copies_of_a_tag(self):
713 | "Prevent recurrence of a bug in the html5lib treebuilder."
714 | content = """<!DOCTYPE html>
715 | <html>
716 | <body>
717 | <article id="a" >
718 | <div><a href="1"></div>
719 | <footer>
720 | <a href="2"></a>
721 | </footer>
722 | </article>
723 | </body>
724 | </html>
725 | """
726 | soup = self.soup(content)
727 | self.assertConnectedness(soup.article)
728 |
729 | def test_basic_namespaces(self):
730 | """Parsers don't need to *understand* namespaces, but at the
731 | very least they should not choke on namespaces or lose
732 | data."""
733 |
734 | markup = b'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" xmlns:svg="http://www.w3.org/2000/svg"><head></head><body><mathml:msqrt>4</mathml:msqrt><b svg:fill="red"></b></body></html>'
735 | soup = self.soup(markup)
736 | assert markup == soup.encode()
737 | html = soup.html
738 | assert 'http://www.w3.org/1999/xhtml' == soup.html['xmlns']
739 | assert 'http://www.w3.org/1998/Math/MathML' == soup.html['xmlns:mathml']
740 | assert 'http://www.w3.org/2000/svg' == soup.html['xmlns:svg']
741 |
742 | def test_multivalued_attribute_value_becomes_list(self):
743 | markup = b'<a class="foo bar">'
744 | soup = self.soup(markup)
745 | assert ['foo', 'bar'] == soup.a['class']
746 |
747 | #
748 | # Generally speaking, tests below this point are more tests of
749 | # Beautiful Soup than tests of the tree builders. But parsers are
750 | # weird, so we run these tests separately for every tree builder
751 | # to detect any differences between them.
752 | #
753 |
754 | def test_can_parse_unicode_document(self):
755 | # A seemingly innocuous document... but it's in Unicode! And
756 | # it contains characters that can't be represented in the
757 | # encoding found in the declaration! The horror!
758 | markup = '<html><head><meta encoding="euc-jp"></head><body>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</body>'
759 | soup = self.soup(markup)
760 | assert 'Sacr\xe9 bleu!' == soup.body.string
761 |
762 | def test_soupstrainer(self):
763 | """Parsers should be able to work with SoupStrainers."""
764 | strainer = SoupStrainer("b")
765 | soup = self.soup("A <b>bold</b> <meta/> <i>statement</i>",
766 | parse_only=strainer)
767 | assert soup.decode() == "<b>bold</b>"
768 |
769 | def test_single_quote_attribute_values_become_double_quotes(self):
770 | self.assert_soup("<foo attr='bar'></foo>",
771 | '<foo attr="bar"></foo>')
772 |
773 | def test_attribute_values_with_nested_quotes_are_left_alone(self):
774 | text = """<foo attr='bar "brawls" happen'>a</foo>"""
775 | self.assert_soup(text)
776 |
777 | def test_attribute_values_with_double_nested_quotes_get_quoted(self):
778 | text = """<foo attr='bar "brawls" happen'>a</foo>"""
779 | soup = self.soup(text)
780 | soup.foo['attr'] = 'Brawls happen at "Bob\'s Bar"'
781 | self.assert_soup(
782 | soup.foo.decode(),
783 | """<foo attr="Brawls happen at "Bob\'s Bar"">a</foo>""")
784 |
785 | def test_ampersand_in_attribute_value_gets_escaped(self):
786 | self.assert_soup('<this is="really messed up & stuff"></this>',
787 | '<this is="really messed up & stuff"></this>')
788 |
789 | self.assert_soup(
790 | '<a href="http://example.org?a=1&b=2;3">foo</a>',
791 | '<a href="http://example.org?a=1&b=2;3">foo</a>')
792 |
793 | def test_escaped_ampersand_in_attribute_value_is_left_alone(self):
794 | self.assert_soup('<a href="http://example.org?a=1&b=2;3"></a>')
795 |
796 | def test_entities_in_strings_converted_during_parsing(self):
797 | # Both XML and HTML entities are converted to Unicode characters
798 | # during parsing.
799 | text = "<p><<sacré bleu!>></p>"
800 | expected = "<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></p>"
801 | self.assert_soup(text, expected)
802 |
803 | def test_smart_quotes_converted_on_the_way_in(self):
804 | # Microsoft smart quotes are converted to Unicode characters during
805 | # parsing.
806 | quote = b"<p>\x91Foo\x92</p>"
807 | soup = self.soup(quote)
808 | assert soup.p.string == "\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}"
809 |
810 | def test_non_breaking_spaces_converted_on_the_way_in(self):
811 | soup = self.soup("<a> </a>")
812 | assert soup.a.string == "\N{NO-BREAK SPACE}" * 2
813 |
814 | def test_entities_converted_on_the_way_out(self):
815 | text = "<p><<sacré bleu!>></p>"
816 | expected = "<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></p>".encode("utf-8")
817 | soup = self.soup(text)
818 | assert soup.p.encode("utf-8") == expected
819 |
820 | def test_real_iso_8859_document(self):
821 | # Smoke test of interrelated functionality, using an
822 | # easy-to-understand document.
823 |
824 | # Here it is in Unicode. Note that it claims to be in ISO-8859-1.
825 | unicode_html = '<html><head><meta content="text/html; charset=ISO-8859-1" http-equiv="Content-type"/></head><body><p>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</p></body></html>'
826 |
827 | # That's because we're going to encode it into ISO-8859-1,
828 | # and use that to test.
829 | iso_latin_html = unicode_html.encode("iso-8859-1")
830 |
831 | # Parse the ISO-8859-1 HTML.
832 | soup = self.soup(iso_latin_html)
833 |
834 | # Encode it to UTF-8.
835 | result = soup.encode("utf-8")
836 |
837 | # What do we expect the result to look like? Well, it would
838 | # look like unicode_html, except that the META tag would say
839 | # UTF-8 instead of ISO-8859-1.
840 | expected = unicode_html.replace("ISO-8859-1", "utf-8")
841 |
842 | # And, of course, it would be in UTF-8, not Unicode.
843 | expected = expected.encode("utf-8")
844 |
845 | # Ta-da!
846 | assert result == expected
847 |
848 | def test_real_shift_jis_document(self):
849 | # Smoke test to make sure the parser can handle a document in
850 | # Shift-JIS encoding, without choking.
851 | shift_jis_html = (
852 | b'<html><head></head><body><pre>'
853 | b'\x82\xb1\x82\xea\x82\xcdShift-JIS\x82\xc5\x83R\x81[\x83f'
854 | b'\x83B\x83\x93\x83O\x82\xb3\x82\xea\x82\xbd\x93\xfa\x96{\x8c'
855 | b'\xea\x82\xcc\x83t\x83@\x83C\x83\x8b\x82\xc5\x82\xb7\x81B'
856 | b'</pre></body></html>')
857 | unicode_html = shift_jis_html.decode("shift-jis")
858 | soup = self.soup(unicode_html)
859 |
860 | # Make sure the parse tree is correctly encoded to various
861 | # encodings.
862 | assert soup.encode("utf-8") == unicode_html.encode("utf-8")
863 | assert soup.encode("euc_jp") == unicode_html.encode("euc_jp")
864 |
865 | def test_real_hebrew_document(self):
866 | # A real-world test to make sure we can convert ISO-8859-9 (a
867 | # Hebrew encoding) to UTF-8.
868 | hebrew_document = b'<html><head><title>Hebrew (ISO 8859-8) in Visual Directionality</title></head><body><h1>Hebrew (ISO 8859-8) in Visual Directionality</h1>\xed\xe5\xec\xf9</body></html>'
869 | soup = self.soup(
870 | hebrew_document, from_encoding="iso8859-8")
871 | # Some tree builders call it iso8859-8, others call it iso-8859-9.
872 | # That's not a difference we really care about.
873 | assert soup.original_encoding in ('iso8859-8', 'iso-8859-8')
874 | assert soup.encode('utf-8') == (
875 | hebrew_document.decode("iso8859-8").encode("utf-8")
876 | )
877 |
878 | def test_meta_tag_reflects_current_encoding(self):
879 | # Here's the <meta> tag saying that a document is
880 | # encoded in Shift-JIS.
881 | meta_tag = ('<meta content="text/html; charset=x-sjis" '
882 | 'http-equiv="Content-type"/>')
883 |
884 | # Here's a document incorporating that meta tag.
885 | shift_jis_html = (
886 | '<html><head>\n%s\n'
887 | '<meta http-equiv="Content-language" content="ja"/>'
888 | '</head><body>Shift-JIS markup goes here.') % meta_tag
889 | soup = self.soup(shift_jis_html)
890 |
891 | # Parse the document, and the charset is seemingly unaffected.
892 | parsed_meta = soup.find('meta', {'http-equiv': 'Content-type'})
893 | content = parsed_meta['content']
894 | assert 'text/html; charset=x-sjis' == content
895 |
896 | # But that value is actually a ContentMetaAttributeValue object.
897 | assert isinstance(content, ContentMetaAttributeValue)
898 |
899 | # And it will take on a value that reflects its current
900 | # encoding.
901 | assert 'text/html; charset=utf8' == content.encode("utf8")
902 |
903 | # For the rest of the story, see TestSubstitutions in
904 | # test_tree.py.
905 |
906 | def test_html5_style_meta_tag_reflects_current_encoding(self):
907 | # Here's the <meta> tag saying that a document is
908 | # encoded in Shift-JIS.
909 | meta_tag = ('<meta id="encoding" charset="x-sjis" />')
910 |
911 | # Here's a document incorporating that meta tag.
912 | shift_jis_html = (
913 | '<html><head>\n%s\n'
914 | '<meta http-equiv="Content-language" content="ja"/>'
915 | '</head><body>Shift-JIS markup goes here.') % meta_tag
916 | soup = self.soup(shift_jis_html)
917 |
918 | # Parse the document, and the charset is seemingly unaffected.
919 | parsed_meta = soup.find('meta', id="encoding")
920 | charset = parsed_meta['charset']
921 | assert 'x-sjis' == charset
922 |
923 | # But that value is actually a CharsetMetaAttributeValue object.
924 | assert isinstance(charset, CharsetMetaAttributeValue)
925 |
926 | # And it will take on a value that reflects its current
927 | # encoding.
928 | assert 'utf8' == charset.encode("utf8")
929 |
930 | def test_python_specific_encodings_not_used_in_charset(self):
931 | # You can encode an HTML document using a Python-specific
932 | # encoding, but that encoding won't be mentioned _inside_ the
933 | # resulting document. Instead, the document will appear to
934 | # have no encoding.
935 | for markup in [
936 | b'<meta charset="utf8"></head>'
937 | b'<meta id="encoding" charset="utf-8" />'
938 | ]:
939 | soup = self.soup(markup)
940 | for encoding in PYTHON_SPECIFIC_ENCODINGS:
941 | if encoding in (
942 | 'idna', 'mbcs', 'oem', 'undefined',
943 | 'string_escape', 'string-escape'
944 | ):
945 | # For one reason or another, these will raise an
946 | # exception if we actually try to use them, so don't
947 | # bother.
948 | continue
949 | encoded = soup.encode(encoding)
950 | assert b'meta charset=""' in encoded
951 | assert encoding.encode("ascii") not in encoded
952 |
953 | def test_tag_with_no_attributes_can_have_attributes_added(self):
954 | data = self.soup("<a>text</a>")
955 | data.a['foo'] = 'bar'
956 | assert '<a foo="bar">text</a>' == data.a.decode()
957 |
958 | def test_closing_tag_with_no_opening_tag(self):
959 | # Without BeautifulSoup.open_tag_counter, the </span> tag will
960 | # cause _popToTag to be called over and over again as we look
961 | # for a <span> tag that wasn't there. The result is that 'text2'
962 | # will show up outside the body of the document.
963 | soup = self.soup("<body><div><p>text1</p></span>text2</div></body>")
964 | assert "<body><div><p>text1</p>text2</div></body>" == soup.body.decode()
965 |
966 | def test_worst_case(self):
967 | """Test the worst case (currently) for linking issues."""
968 |
969 | soup = self.soup(BAD_DOCUMENT)
970 | self.linkage_validator(soup)
971 |
972 |
973 | class XMLTreeBuilderSmokeTest(TreeBuilderSmokeTest):
974 |
975 | def test_pickle_and_unpickle_identity(self):
976 | # Pickling a tree, then unpickling it, yields a tree identical
977 | # to the original.
978 | tree = self.soup("<a><b>foo</a>")
979 | dumped = pickle.dumps(tree, 2)
980 | loaded = pickle.loads(dumped)
981 | assert loaded.__class__ == BeautifulSoup
982 | assert loaded.decode() == tree.decode()
983 |
984 | def test_docstring_generated(self):
985 | soup = self.soup("<root/>")
986 | assert soup.encode() == b'<?xml version="1.0" encoding="utf-8"?>\n<root/>'
987 |
988 | def test_xml_declaration(self):
989 | markup = b"""<?xml version="1.0" encoding="utf8"?>\n<foo/>"""
990 | soup = self.soup(markup)
991 | assert markup == soup.encode("utf8")
992 |
993 | def test_python_specific_encodings_not_used_in_xml_declaration(self):
994 | # You can encode an XML document using a Python-specific
995 | # encoding, but that encoding won't be mentioned _inside_ the
996 | # resulting document.
997 | markup = b"""<?xml version="1.0"?>\n<foo/>"""
998 | soup = self.soup(markup)
999 | for encoding in PYTHON_SPECIFIC_ENCODINGS:
1000 | if encoding in (
1001 | 'idna', 'mbcs', 'oem', 'undefined',
1002 | 'string_escape', 'string-escape'
1003 | ):
1004 | # For one reason or another, these will raise an
1005 | # exception if we actually try to use them, so don't
1006 | # bother.
1007 | continue
1008 | encoded = soup.encode(encoding)
1009 | assert b'<?xml version="1.0"?>' in encoded
1010 | assert encoding.encode("ascii") not in encoded
1011 |
1012 | def test_processing_instruction(self):
1013 | markup = b"""<?xml version="1.0" encoding="utf8"?>\n<?PITarget PIContent?>"""
1014 | soup = self.soup(markup)
1015 | assert markup == soup.encode("utf8")
1016 |
1017 | def test_real_xhtml_document(self):
1018 | """A real XHTML document should come out *exactly* the same as it went in."""
1019 | markup = b"""<?xml version="1.0" encoding="utf-8"?>
1020 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
1021 | <html xmlns="http://www.w3.org/1999/xhtml">
1022 | <head><title>Hello.</title></head>
1023 | <body>Goodbye.</body>
1024 | </html>"""
1025 | soup = self.soup(markup)
1026 | assert soup.encode("utf-8") == markup
1027 |
1028 | def test_nested_namespaces(self):
1029 | doc = b"""<?xml version="1.0" encoding="utf-8"?>
1030 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1031 | <parent xmlns="http://ns1/">
1032 | <child xmlns="http://ns2/" xmlns:ns3="http://ns3/">
1033 | <grandchild ns3:attr="value" xmlns="http://ns4/"/>
1034 | </child>
1035 | </parent>"""
1036 | soup = self.soup(doc)
1037 | assert doc == soup.encode()
1038 |
1039 | def test_formatter_processes_script_tag_for_xml_documents(self):
1040 | doc = """
1041 | <script type="text/javascript">
1042 | </script>
1043 | """
1044 | soup = BeautifulSoup(doc, "lxml-xml")
1045 | # lxml would have stripped this while parsing, but we can add
1046 | # it later.
1047 | soup.script.string = 'console.log("< < hey > > ");'
1048 | encoded = soup.encode()
1049 | assert b"< < hey > >" in encoded
1050 |
1051 | def test_can_parse_unicode_document(self):
1052 | markup = '<?xml version="1.0" encoding="euc-jp"><root>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</root>'
1053 | soup = self.soup(markup)
1054 | assert 'Sacr\xe9 bleu!' == soup.root.string
1055 |
1056 | def test_can_parse_unicode_document_begining_with_bom(self):
1057 | markup = '\N{BYTE ORDER MARK}<?xml version="1.0" encoding="euc-jp"><root>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</root>'
1058 | soup = self.soup(markup)
1059 | assert 'Sacr\xe9 bleu!' == soup.root.string
1060 |
1061 | def test_popping_namespaced_tag(self):
1062 | markup = '<rss xmlns:dc="foo"><dc:creator>b</dc:creator><dc:date>2012-07-02T20:33:42Z</dc:date><dc:rights>c</dc:rights><image>d</image></rss>'
1063 | soup = self.soup(markup)
1064 | assert str(soup.rss) == markup
1065 |
1066 | def test_docstring_includes_correct_encoding(self):
1067 | soup = self.soup("<root/>")
1068 | assert soup.encode("latin1") == b'<?xml version="1.0" encoding="latin1"?>\n<root/>'
1069 |
1070 | def test_large_xml_document(self):
1071 | """A large XML document should come out the same as it went in."""
1072 | markup = (b'<?xml version="1.0" encoding="utf-8"?>\n<root>'
1073 | + b'0' * (2**12)
1074 | + b'</root>')
1075 | soup = self.soup(markup)
1076 | assert soup.encode("utf-8") == markup
1077 |
1078 | def test_tags_are_empty_element_if_and_only_if_they_are_empty(self):
1079 | self.assert_soup("<p>", "<p/>")
1080 | self.assert_soup("<p>foo</p>")
1081 |
1082 | def test_namespaces_are_preserved(self):
1083 | markup = '<root xmlns:a="http://example.com/" xmlns:b="http://example.net/"><a:foo>This tag is in the a namespace</a:foo><b:foo>This tag is in the b namespace</b:foo></root>'
1084 | soup = self.soup(markup)
1085 | root = soup.root
1086 | assert "http://example.com/" == root['xmlns:a']
1087 | assert "http://example.net/" == root['xmlns:b']
1088 |
1089 | def test_closing_namespaced_tag(self):
1090 | markup = '<p xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>20010504</dc:date></p>'
1091 | soup = self.soup(markup)
1092 | assert str(soup.p) == markup
1093 |
1094 | def test_namespaced_attributes(self):
1095 | markup = '<foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><bar xsi:schemaLocation="http://www.example.com"/></foo>'
1096 | soup = self.soup(markup)
1097 | assert str(soup.foo) == markup
1098 |
1099 | def test_namespaced_attributes_xml_namespace(self):
1100 | markup = '<foo xml:lang="fr">bar</foo>'
1101 | soup = self.soup(markup)
1102 | assert str(soup.foo) == markup
1103 |
1104 | def test_find_by_prefixed_name(self):
1105 | doc = """<?xml version="1.0" encoding="utf-8"?>
1106 | <Document xmlns="http://example.com/ns0"
1107 | xmlns:ns1="http://example.com/ns1"
1108 | xmlns:ns2="http://example.com/ns2">
1109 | <ns1:tag>foo</ns1:tag>
1110 | <ns1:tag>bar</ns1:tag>
1111 | <ns2:tag key="value">baz</ns2:tag>
1112 | </Document>
1113 | """
1114 | soup = self.soup(doc)
1115 |
1116 | # There are three <tag> tags.
1117 | assert 3 == len(soup.find_all('tag'))
1118 |
1119 | # But two of them are ns1:tag and one of them is ns2:tag.
1120 | assert 2 == len(soup.find_all('ns1:tag'))
1121 | assert 1 == len(soup.find_all('ns2:tag'))
1122 |
1123 | assert 1, len(soup.find_all('ns2:tag', key='value'))
1124 | assert 3, len(soup.find_all(['ns1:tag', 'ns2:tag']))
1125 |
1126 | def test_copy_tag_preserves_namespace(self):
1127 | xml = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1128 | <w:document xmlns:w="http://example.com/ns0"/>"""
1129 |
1130 | soup = self.soup(xml)
1131 | tag = soup.document
1132 | duplicate = copy.copy(tag)
1133 |
1134 | # The two tags have the same namespace prefix.
1135 | assert tag.prefix == duplicate.prefix
1136 |
1137 | def test_worst_case(self):
1138 | """Test the worst case (currently) for linking issues."""
1139 |
1140 | soup = self.soup(BAD_DOCUMENT)
1141 | self.linkage_validator(soup)
1142 |
1143 |
1144 | class HTML5TreeBuilderSmokeTest(HTMLTreeBuilderSmokeTest):
1145 | """Smoke test for a tree builder that supports HTML5."""
1146 |
1147 | def test_real_xhtml_document(self):
1148 | # Since XHTML is not HTML5, HTML5 parsers are not tested to handle
1149 | # XHTML documents in any particular way.
1150 | pass
1151 |
1152 | def test_html_tags_have_namespace(self):
1153 | markup = "<a>"
1154 | soup = self.soup(markup)
1155 | assert "http://www.w3.org/1999/xhtml" == soup.a.namespace
1156 |
1157 | def test_svg_tags_have_namespace(self):
1158 | markup = '<svg><circle/></svg>'
1159 | soup = self.soup(markup)
1160 | namespace = "http://www.w3.org/2000/svg"
1161 | assert namespace == soup.svg.namespace
1162 | assert namespace == soup.circle.namespace
1163 |
1164 |
1165 | def test_mathml_tags_have_namespace(self):
1166 | markup = '<math><msqrt>5</msqrt></math>'
1167 | soup = self.soup(markup)
1168 | namespace = 'http://www.w3.org/1998/Math/MathML'
1169 | assert namespace == soup.math.namespace
1170 | assert namespace == soup.msqrt.namespace
1171 |
1172 | def test_xml_declaration_becomes_comment(self):
1173 | markup = '<?xml version="1.0" encoding="utf-8"?><html></html>'
1174 | soup = self.soup(markup)
1175 | assert isinstance(soup.contents[0], Comment)
1176 | assert soup.contents[0] == '?xml version="1.0" encoding="utf-8"?'
1177 | assert "html" == soup.contents[0].next_element.name
1178 |
```