From 9d299559c6a0fc52c2a7aacaa73bc7b72f547c5d Mon Sep 17 00:00:00 2001 From: diwaaudi Date: Fri, 9 Jan 2026 06:11:54 +0530 Subject: [PATCH 1/5] Replace assert in command.execute Signed-off-by: diwaaudi --- .venv/bin/Activate.ps1 | 247 + .venv/bin/about | 8 + .venv/bin/activate | 63 + .venv/bin/activate.csh | 26 + .venv/bin/activate.fish | 69 + .venv/bin/black | 8 + .venv/bin/blackd | 8 + .venv/bin/docutils | 8 + .venv/bin/isort | 8 + .venv/bin/isort-identify-imports | 8 + .venv/bin/keyring | 8 + .venv/bin/markdown-it | 8 + .venv/bin/normalizer | 8 + .venv/bin/pip | 8 + .venv/bin/pip3 | 8 + .venv/bin/pip3.11 | 8 + .venv/bin/pkginfo | 8 + .venv/bin/py.test | 8 + .venv/bin/pycodestyle | 8 + .venv/bin/pygmentize | 8 + .venv/bin/pytest | 8 + .venv/bin/python | 1 + .venv/bin/python3 | 1 + .venv/bin/python3.11 | 1 + .venv/bin/rst2html.py | 23 + .venv/bin/rst2html4.py | 26 + .venv/bin/rst2html5.py | 33 + .venv/bin/rst2latex.py | 26 + .venv/bin/rst2man.py | 27 + .venv/bin/rst2odt.py | 28 + .venv/bin/rst2odt_prepstyles.py | 65 + .venv/bin/rst2pseudoxml.py | 23 + .venv/bin/rst2s5.py | 24 + .venv/bin/rst2xetex.py | 27 + .venv/bin/rst2xml.py | 23 + .venv/bin/rstpep2html.py | 25 + .venv/bin/twine | 8 + ...23d__mypyc.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 3993856 bytes .../Jinja2-3.1.2.dist-info/INSTALLER | 1 + .../Jinja2-3.1.2.dist-info/LICENSE.rst | 28 + .../Jinja2-3.1.2.dist-info/METADATA | 113 + .../Jinja2-3.1.2.dist-info/RECORD | 59 + .../Jinja2-3.1.2.dist-info/REQUESTED | 0 .../Jinja2-3.1.2.dist-info/WHEEL | 5 + .../Jinja2-3.1.2.dist-info/entry_points.txt | 2 + .../Jinja2-3.1.2.dist-info/top_level.txt | 1 + .../MarkupSafe-2.1.2.dist-info/INSTALLER | 1 + .../MarkupSafe-2.1.2.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.1.2.dist-info/METADATA | 98 + .../MarkupSafe-2.1.2.dist-info/RECORD | 15 + .../MarkupSafe-2.1.2.dist-info/REQUESTED | 0 .../MarkupSafe-2.1.2.dist-info/WHEEL | 6 + .../MarkupSafe-2.1.2.dist-info/top_level.txt | 1 + .../Pygments-2.14.0.dist-info/AUTHORS | 264 + .../Pygments-2.14.0.dist-info/INSTALLER | 1 + .../Pygments-2.14.0.dist-info/LICENSE | 25 + .../Pygments-2.14.0.dist-info/METADATA | 38 + .../Pygments-2.14.0.dist-info/RECORD | 608 + .../Pygments-2.14.0.dist-info/REQUESTED | 0 .../Pygments-2.14.0.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../Pygments-2.14.0.dist-info/top_level.txt | 1 + .../SecretStorage-3.3.3.dist-info/INSTALLER | 1 + .../SecretStorage-3.3.3.dist-info/LICENSE | 25 + .../SecretStorage-3.3.3.dist-info/METADATA | 114 + .../SecretStorage-3.3.3.dist-info/RECORD | 22 + .../SecretStorage-3.3.3.dist-info/REQUESTED | 0 .../SecretStorage-3.3.3.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/_black_version.py | 1 + ...fi_backend.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 332440 bytes .../site-packages/_distutils_hack/__init__.py | 222 + .../site-packages/_distutils_hack/override.py | 1 + .../site-packages/_pytest/__init__.py | 13 + .../site-packages/_pytest/_argcomplete.py | 117 + .../site-packages/_pytest/_code/__init__.py | 26 + .../site-packages/_pytest/_code/code.py | 1567 + .../site-packages/_pytest/_code/source.py | 225 + .../site-packages/_pytest/_io/__init__.py | 10 + .../site-packages/_pytest/_io/pprint.py | 673 + .../site-packages/_pytest/_io/saferepr.py | 130 + .../_pytest/_io/terminalwriter.py | 254 + .../site-packages/_pytest/_io/wcwidth.py | 57 + .../site-packages/_pytest/_py/__init__.py | 0 .../site-packages/_pytest/_py/error.py | 119 + .../site-packages/_pytest/_py/path.py | 1475 + .../site-packages/_pytest/_version.py | 34 + .../_pytest/assertion/__init__.py | 208 + .../_pytest/assertion/rewrite.py | 1216 + .../_pytest/assertion/truncate.py | 137 + .../site-packages/_pytest/assertion/util.py | 621 + .../site-packages/_pytest/cacheprovider.py | 625 + .../site-packages/_pytest/capture.py | 1144 + .../site-packages/_pytest/compat.py | 333 + .../site-packages/_pytest/config/__init__.py | 2029 ++ .../_pytest/config/argparsing.py | 535 + .../site-packages/_pytest/config/compat.py | 85 + .../_pytest/config/exceptions.py | 13 + .../site-packages/_pytest/config/findpaths.py | 239 + .../site-packages/_pytest/debugging.py | 407 + .../site-packages/_pytest/deprecated.py | 91 + .../site-packages/_pytest/doctest.py | 754 + .../site-packages/_pytest/faulthandler.py | 105 + .../site-packages/_pytest/fixtures.py | 2017 ++ .../site-packages/_pytest/freeze_support.py | 45 + .../site-packages/_pytest/helpconfig.py | 283 + .../site-packages/_pytest/hookspec.py | 1333 + .../site-packages/_pytest/junitxml.py | 692 + .../site-packages/_pytest/legacypath.py | 468 + .../site-packages/_pytest/logging.py | 960 + .../python3.11/site-packages/_pytest/main.py | 1076 + .../site-packages/_pytest/mark/__init__.py | 301 + .../site-packages/_pytest/mark/expression.py | 331 + .../site-packages/_pytest/mark/structures.py | 662 + .../site-packages/_pytest/monkeypatch.py | 415 + .../python3.11/site-packages/_pytest/nodes.py | 772 + .../site-packages/_pytest/outcomes.py | 317 + .../site-packages/_pytest/pastebin.py | 117 + .../site-packages/_pytest/pathlib.py | 1055 + .../python3.11/site-packages/_pytest/py.typed | 0 .../site-packages/_pytest/pytester.py | 1775 ++ .../_pytest/pytester_assertions.py | 74 + .../site-packages/_pytest/python.py | 1723 ++ .../site-packages/_pytest/python_api.py | 809 + .../site-packages/_pytest/raises.py | 1519 + .../site-packages/_pytest/recwarn.py | 365 + .../site-packages/_pytest/reports.py | 637 + .../site-packages/_pytest/runner.py | 571 + .../python3.11/site-packages/_pytest/scope.py | 91 + .../site-packages/_pytest/setuponly.py | 98 + .../site-packages/_pytest/setupplan.py | 39 + .../site-packages/_pytest/skipping.py | 316 + .../python3.11/site-packages/_pytest/stash.py | 116 + .../site-packages/_pytest/stepwise.py | 209 + .../site-packages/_pytest/terminal.py | 1643 + .../site-packages/_pytest/threadexception.py | 152 + .../site-packages/_pytest/timing.py | 94 + .../site-packages/_pytest/tmpdir.py | 312 + .../site-packages/_pytest/tracemalloc.py | 24 + .../site-packages/_pytest/unittest.py | 516 + .../_pytest/unraisableexception.py | 163 + .../site-packages/_pytest/warning_types.py | 166 + .../site-packages/_pytest/warnings.py | 152 + .../site-packages/_yaml/__init__.py | 33 + .../INSTALLER | 1 + .../METADATA | 212 + .../aboutcode_toolkit-11.1.1.dist-info/RECORD | 40 + .../REQUESTED | 0 .../aboutcode_toolkit-11.1.1.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../licenses/AUTHORS.rst | 3 + .../licenses/CHANGELOG.rst | 433 + .../licenses/CODE_OF_CONDUCT.rst | 86 + .../licenses/NOTICE | 19 + .../licenses/apache-2.0.LICENSE | 201 + .../top_level.txt | 1 + .../python3.11/site-packages/attr/__init__.py | 104 + .../site-packages/attr/__init__.pyi | 389 + .../lib/python3.11/site-packages/attr/_cmp.py | 160 + .../python3.11/site-packages/attr/_cmp.pyi | 13 + .../python3.11/site-packages/attr/_compat.py | 99 + .../python3.11/site-packages/attr/_config.py | 31 + .../python3.11/site-packages/attr/_funcs.py | 497 + .../python3.11/site-packages/attr/_make.py | 3362 +++ .../site-packages/attr/_next_gen.py | 674 + .../site-packages/attr/_typing_compat.pyi | 15 + .../site-packages/attr/_version_info.py | 89 + .../site-packages/attr/_version_info.pyi | 9 + .../site-packages/attr/converters.py | 162 + .../site-packages/attr/converters.pyi | 19 + .../site-packages/attr/exceptions.py | 95 + .../site-packages/attr/exceptions.pyi | 17 + .../python3.11/site-packages/attr/filters.py | 72 + .../python3.11/site-packages/attr/filters.pyi | 6 + .../python3.11/site-packages/attr/py.typed | 0 .../python3.11/site-packages/attr/setters.py | 79 + .../python3.11/site-packages/attr/setters.pyi | 20 + .../site-packages/attr/validators.py | 748 + .../site-packages/attr/validators.pyi | 140 + .../site-packages/attributecode/__init__.py | 116 + .../site-packages/attributecode/__main__.py | 19 + .../site-packages/attributecode/api.py | 94 + .../site-packages/attributecode/attrib.py | 339 + .../attributecode/attrib_util.py | 117 + .../site-packages/attributecode/cmd.py | 966 + .../site-packages/attributecode/gen.py | 382 + .../site-packages/attributecode/licenses.py | 85 + .../site-packages/attributecode/model.py | 2184 ++ .../templates/default_html.template | 84 + .../templates/default_json.template | 18 + .../templates/license_ref.template | 69 + .../attributecode/templates/list.csv | 4 + .../templates/scancode_html.template | 88 + .../site-packages/attributecode/transform.py | 446 + .../site-packages/attributecode/util.py | 873 + .../attrs-25.4.0.dist-info/INSTALLER | 1 + .../attrs-25.4.0.dist-info/METADATA | 235 + .../attrs-25.4.0.dist-info/RECORD | 55 + .../attrs-25.4.0.dist-info/WHEEL | 4 + .../attrs-25.4.0.dist-info/licenses/LICENSE | 21 + .../site-packages/attrs/__init__.py | 72 + .../site-packages/attrs/__init__.pyi | 314 + .../site-packages/attrs/converters.py | 3 + .../site-packages/attrs/exceptions.py | 3 + .../python3.11/site-packages/attrs/filters.py | 3 + .../python3.11/site-packages/attrs/py.typed | 0 .../python3.11/site-packages/attrs/setters.py | 3 + .../site-packages/attrs/validators.py | 3 + .../beautifulsoup4-4.14.3.dist-info/INSTALLER | 1 + .../beautifulsoup4-4.14.3.dist-info/METADATA | 123 + .../beautifulsoup4-4.14.3.dist-info/RECORD | 38 + .../beautifulsoup4-4.14.3.dist-info/REQUESTED | 0 .../beautifulsoup4-4.14.3.dist-info/WHEEL | 4 + .../licenses/AUTHORS | 49 + .../licenses/LICENSE | 31 + .../black-23.1.0.dist-info/INSTALLER | 1 + .../black-23.1.0.dist-info/METADATA | 1610 + .../black-23.1.0.dist-info/RECORD | 115 + .../black-23.1.0.dist-info/REQUESTED | 0 .../black-23.1.0.dist-info/WHEEL | 6 + .../black-23.1.0.dist-info/entry_points.txt | 3 + .../licenses/AUTHORS.md | 195 + .../black-23.1.0.dist-info/licenses/LICENSE | 21 + .../__init__.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8248 bytes .../site-packages/black/__init__.py | 1451 + .../site-packages/black/__main__.py | 3 + .../brackets.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/black/brackets.py | 381 + .../cache.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/cache.py | 97 + .../comments.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/black/comments.py | 334 + .../site-packages/black/concurrency.py | 187 + .../const.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/const.py | 4 + .../python3.11/site-packages/black/debug.py | 47 + .../python3.11/site-packages/black/files.py | 399 + ...ynb_magics.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8280 bytes .../black/handle_ipynb_magics.py | 459 + .../linegen.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/linegen.py | 1517 + .../lines.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/lines.py | 868 + .../mode.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8248 bytes .../python3.11/site-packages/black/mode.py | 225 + .../nodes.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/nodes.py | 873 + .../numerics.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/black/numerics.py | 60 + .../python3.11/site-packages/black/output.py | 105 + .../parsing.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/parsing.py | 277 + .../python3.11/site-packages/black/py.typed | 0 .../python3.11/site-packages/black/report.py | 106 + .../rusty.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/rusty.py | 27 + .../strings.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/strings.py | 280 + .../trans.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../python3.11/site-packages/black/trans.py | 2392 ++ .../site-packages/blackd/__init__.py | 227 + .../site-packages/blackd/__main__.py | 3 + .../site-packages/blackd/middlewares.py | 45 + .../bleach-6.0.0.dist-info/INSTALLER | 1 + .../bleach-6.0.0.dist-info/LICENSE | 13 + .../bleach-6.0.0.dist-info/METADATA | 1222 + .../bleach-6.0.0.dist-info/RECORD | 103 + .../bleach-6.0.0.dist-info/REQUESTED | 0 .../bleach-6.0.0.dist-info/WHEEL | 5 + .../bleach-6.0.0.dist-info/top_level.txt | 1 + .../site-packages/bleach/__init__.py | 125 + .../site-packages/bleach/_vendor/README.rst | 61 + .../site-packages/bleach/_vendor/__init__.py | 0 .../html5lib-1.1.dist-info/AUTHORS.rst | 66 + .../_vendor/html5lib-1.1.dist-info/INSTALLER | 1 + .../_vendor/html5lib-1.1.dist-info/LICENSE | 20 + .../_vendor/html5lib-1.1.dist-info/METADATA | 552 + .../_vendor/html5lib-1.1.dist-info/RECORD | 41 + .../_vendor/html5lib-1.1.dist-info/REQUESTED | 0 .../_vendor/html5lib-1.1.dist-info/WHEEL | 6 + .../html5lib-1.1.dist-info/top_level.txt | 1 + .../bleach/_vendor/html5lib/__init__.py | 35 + .../bleach/_vendor/html5lib/_ihatexml.py | 289 + .../bleach/_vendor/html5lib/_inputstream.py | 918 + .../bleach/_vendor/html5lib/_tokenizer.py | 1735 ++ .../bleach/_vendor/html5lib/_trie/__init__.py | 5 + .../bleach/_vendor/html5lib/_trie/_base.py | 40 + .../bleach/_vendor/html5lib/_trie/py.py | 67 + .../bleach/_vendor/html5lib/_utils.py | 159 + .../bleach/_vendor/html5lib/constants.py | 2946 ++ .../_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../bleach/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../bleach/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../_vendor/html5lib/filters/sanitizer.py | 916 + .../_vendor/html5lib/filters/whitespace.py | 38 + .../bleach/_vendor/html5lib/html5parser.py | 2795 ++ .../bleach/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../_vendor/html5lib/treebuilders/base.py | 417 + .../_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../_vendor/html5lib/treewalkers/base.py | 252 + .../_vendor/html5lib/treewalkers/dom.py | 43 + .../_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../site-packages/bleach/_vendor/parse.py | 1078 + .../bleach/_vendor/parse.py.SHA256SUM | 1 + .../site-packages/bleach/_vendor/vendor.txt | 3 + .../bleach/_vendor/vendor_install.sh | 14 + .../site-packages/bleach/callbacks.py | 32 + .../site-packages/bleach/css_sanitizer.py | 104 + .../site-packages/bleach/html5lib_shim.py | 741 + .../site-packages/bleach/linkifier.py | 633 + .../site-packages/bleach/parse_shim.py | 1 + .../site-packages/bleach/sanitizer.py | 638 + .../site-packages/blib2to3/Grammar.txt | 252 + .../python3.11/site-packages/blib2to3/LICENSE | 254 + .../site-packages/blib2to3/PatternGrammar.txt | 28 + .../python3.11/site-packages/blib2to3/README | 23 + .../site-packages/blib2to3/__init__.py | 1 + .../site-packages/blib2to3/pgen2/__init__.py | 4 + .../conv.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/blib2to3/pgen2/conv.py | 256 + .../driver.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/driver.py | 326 + .../grammar.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/grammar.py | 227 + .../literals.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/literals.py | 68 + .../parse.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/parse.py | 393 + .../pgen.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/blib2to3/pgen2/pgen.py | 433 + .../token.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/token.py | 94 + .../tokenize.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8264 bytes .../site-packages/blib2to3/pgen2/tokenize.py | 688 + .../pygram.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/blib2to3/pygram.py | 218 + .../pytree.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 8256 bytes .../site-packages/blib2to3/pytree.py | 983 + .../boolean.py-4.0.dist-info/CHANGELOG.rst | 129 + .../boolean.py-4.0.dist-info/INSTALLER | 1 + .../boolean.py-4.0.dist-info/LICENSE.txt | 23 + .../boolean.py-4.0.dist-info/METADATA | 42 + .../boolean.py-4.0.dist-info/README.rst | 114 + .../boolean.py-4.0.dist-info/RECORD | 15 + .../boolean.py-4.0.dist-info/REQUESTED | 0 .../boolean.py-4.0.dist-info/WHEEL | 5 + .../boolean.py-4.0.dist-info/top_level.txt | 1 + .../site-packages/boolean/__init__.py | 30 + .../site-packages/boolean/boolean.py | 1600 + .../site-packages/boolean/test_boolean.py | 1330 + .../python3.11/site-packages/bs4/__init__.py | 1174 + .../site-packages/bs4/_deprecation.py | 80 + .../python3.11/site-packages/bs4/_typing.py | 205 + .../python3.11/site-packages/bs4/_warnings.py | 98 + .../site-packages/bs4/builder/__init__.py | 848 + .../site-packages/bs4/builder/_html5lib.py | 611 + .../site-packages/bs4/builder/_htmlparser.py | 462 + .../site-packages/bs4/builder/_lxml.py | 501 + .venv/lib/python3.11/site-packages/bs4/css.py | 339 + .../python3.11/site-packages/bs4/dammit.py | 1516 + .../python3.11/site-packages/bs4/diagnose.py | 268 + .../python3.11/site-packages/bs4/element.py | 3211 ++ .../site-packages/bs4/exceptions.py | 28 + .../python3.11/site-packages/bs4/filter.py | 764 + .../python3.11/site-packages/bs4/formatter.py | 276 + .../lib/python3.11/site-packages/bs4/py.typed | 0 .../certifi-2026.1.4.dist-info/INSTALLER | 1 + .../certifi-2026.1.4.dist-info/METADATA | 78 + .../certifi-2026.1.4.dist-info/RECORD | 14 + .../certifi-2026.1.4.dist-info/WHEEL | 5 + .../licenses/LICENSE | 20 + .../certifi-2026.1.4.dist-info/top_level.txt | 1 + .../site-packages/certifi/__init__.py | 4 + .../site-packages/certifi/__main__.py | 12 + .../site-packages/certifi/cacert.pem | 4468 +++ .../python3.11/site-packages/certifi/core.py | 83 + .../python3.11/site-packages/certifi/py.typed | 0 .../cffi-2.0.0.dist-info/INSTALLER | 1 + .../cffi-2.0.0.dist-info/METADATA | 68 + .../site-packages/cffi-2.0.0.dist-info/RECORD | 50 + .../cffi-2.0.0.dist-info/REQUESTED | 0 .../site-packages/cffi-2.0.0.dist-info/WHEEL | 6 + .../cffi-2.0.0.dist-info/entry_points.txt | 2 + .../cffi-2.0.0.dist-info/licenses/AUTHORS | 8 + .../cffi-2.0.0.dist-info/licenses/LICENSE | 23 + .../cffi-2.0.0.dist-info/top_level.txt | 2 + .../python3.11/site-packages/cffi/__init__.py | 14 + .../site-packages/cffi/_cffi_errors.h | 149 + .../site-packages/cffi/_cffi_include.h | 389 + .../site-packages/cffi/_embedding.h | 550 + .../site-packages/cffi/_imp_emulation.py | 83 + .../site-packages/cffi/_shimmed_dist_utils.py | 45 + .../lib/python3.11/site-packages/cffi/api.py | 967 + .../site-packages/cffi/backend_ctypes.py | 1121 + .../site-packages/cffi/cffi_opcode.py | 187 + .../site-packages/cffi/commontypes.py | 82 + .../python3.11/site-packages/cffi/cparser.py | 1015 + .../python3.11/site-packages/cffi/error.py | 31 + .../site-packages/cffi/ffiplatform.py | 113 + .../lib/python3.11/site-packages/cffi/lock.py | 30 + .../python3.11/site-packages/cffi/model.py | 618 + .../site-packages/cffi/parse_c_type.h | 181 + .../site-packages/cffi/pkgconfig.py | 121 + .../site-packages/cffi/recompiler.py | 1598 + .../site-packages/cffi/setuptools_ext.py | 229 + .../site-packages/cffi/vengine_cpy.py | 1087 + .../site-packages/cffi/vengine_gen.py | 679 + .../python3.11/site-packages/cffi/verifier.py | 306 + .../INSTALLER | 1 + .../METADATA | 764 + .../charset_normalizer-3.4.4.dist-info/RECORD | 35 + .../charset_normalizer-3.4.4.dist-info/WHEEL | 7 + .../entry_points.txt | 2 + .../licenses/LICENSE | 21 + .../top_level.txt | 1 + .../charset_normalizer/__init__.py | 48 + .../charset_normalizer/__main__.py | 6 + .../site-packages/charset_normalizer/api.py | 669 + .../site-packages/charset_normalizer/cd.py | 395 + .../charset_normalizer/cli/__init__.py | 8 + .../charset_normalizer/cli/__main__.py | 381 + .../charset_normalizer/constant.py | 2015 ++ .../charset_normalizer/legacy.py | 80 + .../md.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 15912 bytes .../site-packages/charset_normalizer/md.py | 635 + .../md__mypyc.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 282232 bytes .../charset_normalizer/models.py | 360 + .../site-packages/charset_normalizer/py.typed | 0 .../site-packages/charset_normalizer/utils.py | 414 + .../charset_normalizer/version.py | 8 + .../click-8.3.1.dist-info/INSTALLER | 1 + .../click-8.3.1.dist-info/METADATA | 84 + .../click-8.3.1.dist-info/RECORD | 40 + .../site-packages/click-8.3.1.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 28 + .../site-packages/click/__init__.py | 123 + .../python3.11/site-packages/click/_compat.py | 622 + .../site-packages/click/_termui_impl.py | 852 + .../site-packages/click/_textwrap.py | 51 + .../python3.11/site-packages/click/_utils.py | 36 + .../site-packages/click/_winconsole.py | 296 + .../python3.11/site-packages/click/core.py | 3415 +++ .../site-packages/click/decorators.py | 551 + .../site-packages/click/exceptions.py | 308 + .../site-packages/click/formatting.py | 301 + .../python3.11/site-packages/click/globals.py | 67 + .../python3.11/site-packages/click/parser.py | 532 + .../python3.11/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 667 + .../python3.11/site-packages/click/termui.py | 883 + .../python3.11/site-packages/click/testing.py | 577 + .../python3.11/site-packages/click/types.py | 1209 + .../python3.11/site-packages/click/utils.py | 627 + .../cryptography-39.0.1.dist-info/INSTALLER | 1 + .../cryptography-39.0.1.dist-info/LICENSE | 6 + .../LICENSE.APACHE | 202 + .../cryptography-39.0.1.dist-info/LICENSE.BSD | 27 + .../cryptography-39.0.1.dist-info/LICENSE.PSF | 41 + .../cryptography-39.0.1.dist-info/METADATA | 146 + .../cryptography-39.0.1.dist-info/RECORD | 181 + .../cryptography-39.0.1.dist-info/REQUESTED | 0 .../cryptography-39.0.1.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/cryptography/__about__.py | 15 + .../site-packages/cryptography/__init__.py | 25 + .../site-packages/cryptography/exceptions.py | 68 + .../site-packages/cryptography/fernet.py | 220 + .../cryptography/hazmat/__init__.py | 10 + .../site-packages/cryptography/hazmat/_oid.py | 293 + .../cryptography/hazmat/backends/__init__.py | 10 + .../hazmat/backends/openssl/__init__.py | 8 + .../hazmat/backends/openssl/aead.py | 306 + .../hazmat/backends/openssl/backend.py | 2514 ++ .../hazmat/backends/openssl/ciphers.py | 281 + .../hazmat/backends/openssl/cmac.py | 87 + .../hazmat/backends/openssl/decode_asn1.py | 31 + .../hazmat/backends/openssl/dh.py | 317 + .../hazmat/backends/openssl/dsa.py | 236 + .../hazmat/backends/openssl/ec.py | 315 + .../hazmat/backends/openssl/ed25519.py | 155 + .../hazmat/backends/openssl/ed448.py | 156 + .../hazmat/backends/openssl/hashes.py | 86 + .../hazmat/backends/openssl/hmac.py | 84 + .../hazmat/backends/openssl/poly1305.py | 67 + .../hazmat/backends/openssl/rsa.py | 588 + .../hazmat/backends/openssl/utils.py | 52 + .../hazmat/backends/openssl/x25519.py | 132 + .../hazmat/backends/openssl/x448.py | 117 + .../cryptography/hazmat/bindings/__init__.py | 3 + .../hazmat/bindings/_openssl.abi3.so | Bin 0 -> 8580376 bytes .../cryptography/hazmat/bindings/_openssl.pyi | 8 + .../hazmat/bindings/_rust.abi3.so | Bin 0 -> 3715672 bytes .../hazmat/bindings/_rust/__init__.pyi | 34 + .../hazmat/bindings/_rust/asn1.pyi | 16 + .../hazmat/bindings/_rust/ocsp.pyi | 25 + .../hazmat/bindings/_rust/pkcs7.pyi | 15 + .../hazmat/bindings/_rust/x509.pyi | 42 + .../hazmat/bindings/openssl/__init__.py | 3 + .../hazmat/bindings/openssl/_conditional.py | 360 + .../hazmat/bindings/openssl/binding.py | 244 + .../hazmat/primitives/__init__.py | 3 + .../hazmat/primitives/_asymmetric.py | 17 + .../hazmat/primitives/_cipheralgorithm.py | 43 + .../hazmat/primitives/_serialization.py | 168 + .../hazmat/primitives/asymmetric/__init__.py | 3 + .../hazmat/primitives/asymmetric/dh.py | 251 + .../hazmat/primitives/asymmetric/dsa.py | 288 + .../hazmat/primitives/asymmetric/ec.py | 483 + .../hazmat/primitives/asymmetric/ed25519.py | 91 + .../hazmat/primitives/asymmetric/ed448.py | 87 + .../hazmat/primitives/asymmetric/padding.py | 101 + .../hazmat/primitives/asymmetric/rsa.py | 432 + .../hazmat/primitives/asymmetric/types.py | 68 + .../hazmat/primitives/asymmetric/utils.py | 23 + .../hazmat/primitives/asymmetric/x25519.py | 81 + .../hazmat/primitives/asymmetric/x448.py | 81 + .../hazmat/primitives/ciphers/__init__.py | 26 + .../hazmat/primitives/ciphers/aead.py | 374 + .../hazmat/primitives/ciphers/algorithms.py | 227 + .../hazmat/primitives/ciphers/base.py | 269 + .../hazmat/primitives/ciphers/modes.py | 275 + .../cryptography/hazmat/primitives/cmac.py | 64 + .../hazmat/primitives/constant_time.py | 13 + .../cryptography/hazmat/primitives/hashes.py | 261 + .../cryptography/hazmat/primitives/hmac.py | 70 + .../hazmat/primitives/kdf/__init__.py | 22 + .../hazmat/primitives/kdf/concatkdf.py | 127 + .../hazmat/primitives/kdf/hkdf.py | 100 + .../hazmat/primitives/kdf/kbkdf.py | 297 + .../hazmat/primitives/kdf/pbkdf2.py | 65 + .../hazmat/primitives/kdf/scrypt.py | 73 + .../hazmat/primitives/kdf/x963kdf.py | 62 + .../cryptography/hazmat/primitives/keywrap.py | 176 + .../cryptography/hazmat/primitives/padding.py | 224 + .../hazmat/primitives/poly1305.py | 60 + .../primitives/serialization/__init__.py | 46 + .../hazmat/primitives/serialization/base.py | 72 + .../hazmat/primitives/serialization/pkcs12.py | 226 + .../hazmat/primitives/serialization/pkcs7.py | 219 + .../hazmat/primitives/serialization/ssh.py | 758 + .../hazmat/primitives/twofactor/__init__.py | 7 + .../hazmat/primitives/twofactor/hotp.py | 91 + .../hazmat/primitives/twofactor/totp.py | 48 + .../site-packages/cryptography/py.typed | 0 .../site-packages/cryptography/utils.py | 132 + .../cryptography/x509/__init__.py | 250 + .../site-packages/cryptography/x509/base.py | 1131 + .../x509/certificate_transparency.py | 96 + .../cryptography/x509/extensions.py | 2113 ++ .../cryptography/x509/general_name.py | 284 + .../site-packages/cryptography/x509/name.py | 460 + .../site-packages/cryptography/x509/ocsp.py | 621 + .../site-packages/cryptography/x509/oid.py | 31 + .../site-packages/distutils-precedence.pth | 1 + .../docutils-0.19.dist-info/COPYING.txt | 157 + .../docutils-0.19.dist-info/INSTALLER | 1 + .../docutils-0.19.dist-info/METADATA | 64 + .../docutils-0.19.dist-info/RECORD | 350 + .../docutils-0.19.dist-info/REQUESTED | 0 .../docutils-0.19.dist-info/SOURCES.html | 681 + .../docutils-0.19.dist-info/WHEEL | 5 + .../dependency_links.html | 24 + .../docutils-0.19.dist-info/entry_points.txt | 3 + .../docutils-0.19.dist-info/top_level.html | 24 + .../docutils-0.19.dist-info/top_level.txt | 1 + .../site-packages/docutils/__init__.py | 284 + .../site-packages/docutils/__main__.py | 96 + .../python3.11/site-packages/docutils/core.py | 714 + .../site-packages/docutils/examples.py | 99 + .../site-packages/docutils/frontend.py | 951 + .../python3.11/site-packages/docutils/io.py | 611 + .../docutils/languages/__init__.py | 83 + .../site-packages/docutils/languages/af.py | 58 + .../site-packages/docutils/languages/ar.py | 60 + .../site-packages/docutils/languages/ca.py | 60 + .../site-packages/docutils/languages/cs.py | 60 + .../site-packages/docutils/languages/da.py | 61 + .../site-packages/docutils/languages/de.py | 58 + .../site-packages/docutils/languages/en.py | 60 + .../site-packages/docutils/languages/eo.py | 61 + .../site-packages/docutils/languages/es.py | 58 + .../site-packages/docutils/languages/fa.py | 60 + .../site-packages/docutils/languages/fi.py | 60 + .../site-packages/docutils/languages/fr.py | 58 + .../site-packages/docutils/languages/gl.py | 62 + .../site-packages/docutils/languages/he.py | 61 + .../site-packages/docutils/languages/it.py | 58 + .../site-packages/docutils/languages/ja.py | 60 + .../site-packages/docutils/languages/ko.py | 60 + .../site-packages/docutils/languages/lt.py | 60 + .../site-packages/docutils/languages/lv.py | 59 + .../site-packages/docutils/languages/nl.py | 60 + .../site-packages/docutils/languages/pl.py | 60 + .../site-packages/docutils/languages/pt_br.py | 60 + .../site-packages/docutils/languages/ru.py | 58 + .../site-packages/docutils/languages/sk.py | 58 + .../site-packages/docutils/languages/sv.py | 59 + .../site-packages/docutils/languages/zh_cn.py | 66 + .../site-packages/docutils/languages/zh_tw.py | 65 + .../site-packages/docutils/nodes.py | 2314 ++ .../docutils/parsers/__init__.py | 92 + .../docutils/parsers/commonmark_wrapper.py | 56 + .../site-packages/docutils/parsers/null.py | 20 + .../docutils/parsers/recommonmark_wrapper.py | 136 + .../docutils/parsers/rst/__init__.py | 414 + .../parsers/rst/directives/__init__.py | 460 + .../parsers/rst/directives/admonitions.py | 99 + .../docutils/parsers/rst/directives/body.py | 304 + .../docutils/parsers/rst/directives/html.py | 21 + .../docutils/parsers/rst/directives/images.py | 163 + .../docutils/parsers/rst/directives/misc.py | 635 + .../docutils/parsers/rst/directives/parts.py | 126 + .../parsers/rst/directives/references.py | 29 + .../docutils/parsers/rst/directives/tables.py | 513 + .../docutils/parsers/rst/include/README.txt | 17 + .../docutils/parsers/rst/include/isoamsa.txt | 162 + .../docutils/parsers/rst/include/isoamsb.txt | 126 + .../docutils/parsers/rst/include/isoamsc.txt | 29 + .../docutils/parsers/rst/include/isoamsn.txt | 96 + .../docutils/parsers/rst/include/isoamso.txt | 62 + .../docutils/parsers/rst/include/isoamsr.txt | 191 + .../docutils/parsers/rst/include/isobox.txt | 46 + .../docutils/parsers/rst/include/isocyr1.txt | 73 + .../docutils/parsers/rst/include/isocyr2.txt | 32 + .../docutils/parsers/rst/include/isodia.txt | 20 + .../docutils/parsers/rst/include/isogrk1.txt | 55 + .../docutils/parsers/rst/include/isogrk2.txt | 26 + .../docutils/parsers/rst/include/isogrk3.txt | 52 + .../parsers/rst/include/isogrk4-wide.txt | 49 + .../docutils/parsers/rst/include/isogrk4.txt | 8 + .../docutils/parsers/rst/include/isolat1.txt | 68 + .../docutils/parsers/rst/include/isolat2.txt | 128 + .../parsers/rst/include/isomfrk-wide.txt | 58 + .../docutils/parsers/rst/include/isomfrk.txt | 11 + .../parsers/rst/include/isomopf-wide.txt | 32 + .../docutils/parsers/rst/include/isomopf.txt | 13 + .../parsers/rst/include/isomscr-wide.txt | 58 + .../docutils/parsers/rst/include/isomscr.txt | 17 + .../docutils/parsers/rst/include/isonum.txt | 82 + .../docutils/parsers/rst/include/isopub.txt | 90 + .../docutils/parsers/rst/include/isotech.txt | 168 + .../docutils/parsers/rst/include/mmlalias.txt | 554 + .../parsers/rst/include/mmlextra-wide.txt | 113 + .../docutils/parsers/rst/include/mmlextra.txt | 87 + .../docutils/parsers/rst/include/s5defs.txt | 68 + .../parsers/rst/include/xhtml1-lat1.txt | 102 + .../parsers/rst/include/xhtml1-special.txt | 37 + .../parsers/rst/include/xhtml1-symbol.txt | 130 + .../parsers/rst/languages/__init__.py | 40 + .../docutils/parsers/rst/languages/af.py | 107 + .../docutils/parsers/rst/languages/ar.py | 99 + .../docutils/parsers/rst/languages/ca.py | 126 + .../docutils/parsers/rst/languages/cs.py | 110 + .../docutils/parsers/rst/languages/da.py | 113 + .../docutils/parsers/rst/languages/de.py | 106 + .../docutils/parsers/rst/languages/en.py | 111 + .../docutils/parsers/rst/languages/eo.py | 118 + .../docutils/parsers/rst/languages/es.py | 122 + .../docutils/parsers/rst/languages/fa.py | 102 + .../docutils/parsers/rst/languages/fi.py | 98 + .../docutils/parsers/rst/languages/fr.py | 104 + .../docutils/parsers/rst/languages/gl.py | 111 + .../docutils/parsers/rst/languages/he.py | 109 + .../docutils/parsers/rst/languages/it.py | 98 + .../docutils/parsers/rst/languages/ja.py | 119 + .../docutils/parsers/rst/languages/ko.py | 111 + .../docutils/parsers/rst/languages/lt.py | 109 + .../docutils/parsers/rst/languages/lv.py | 108 + .../docutils/parsers/rst/languages/nl.py | 113 + .../docutils/parsers/rst/languages/pl.py | 100 + .../docutils/parsers/rst/languages/pt_br.py | 109 + .../docutils/parsers/rst/languages/ru.py | 90 + .../docutils/parsers/rst/languages/sk.py | 96 + .../docutils/parsers/rst/languages/sv.py | 96 + .../docutils/parsers/rst/languages/zh_cn.py | 104 + .../docutils/parsers/rst/languages/zh_tw.py | 109 + .../docutils/parsers/rst/roles.py | 439 + .../docutils/parsers/rst/states.py | 3135 ++ .../docutils/parsers/rst/tableparser.py | 539 + .../docutils/readers/__init__.py | 113 + .../site-packages/docutils/readers/doctree.py | 46 + .../site-packages/docutils/readers/pep.py | 48 + .../docutils/readers/standalone.py | 65 + .../site-packages/docutils/statemachine.py | 1525 + .../docutils/transforms/__init__.py | 174 + .../docutils/transforms/components.py | 54 + .../docutils/transforms/frontmatter.py | 545 + .../site-packages/docutils/transforms/misc.py | 144 + .../docutils/transforms/parts.py | 176 + .../site-packages/docutils/transforms/peps.py | 308 + .../docutils/transforms/references.py | 919 + .../docutils/transforms/universal.py | 338 + .../docutils/transforms/writer_aux.py | 99 + .../site-packages/docutils/utils/__init__.py | 821 + .../docutils/utils/code_analyzer.py | 138 + .../docutils/utils/error_reporting.py | 222 + .../docutils/utils/math/__init__.py | 52 + .../docutils/utils/math/latex2mathml.py | 1430 + .../docutils/utils/math/math2html.py | 3171 ++ .../docutils/utils/math/tex2mathml_extern.py | 152 + .../docutils/utils/math/tex2unichar.py | 704 + .../docutils/utils/math/unichar2tex.py | 808 + .../docutils/utils/punctuation_chars.py | 122 + .../site-packages/docutils/utils/roman.py | 86 + .../docutils/utils/smartquotes.py | 991 + .../docutils/utils/urischemes.py | 138 + .../docutils/writers/__init__.py | 151 + .../docutils/writers/_html_base.py | 1781 ++ .../docutils/writers/docutils_xml.py | 188 + .../docutils/writers/html4css1/__init__.py | 947 + .../docutils/writers/html4css1/html4css1.css | 349 + .../docutils/writers/html4css1/template.txt | 8 + .../writers/html5_polyglot/__init__.py | 454 + .../docutils/writers/html5_polyglot/math.css | 332 + .../writers/html5_polyglot/minimal.css | 276 + .../docutils/writers/html5_polyglot/plain.css | 312 + .../writers/html5_polyglot/responsive.css | 495 + .../writers/html5_polyglot/template.txt | 8 + .../writers/html5_polyglot/tuftig.css | 568 + .../docutils/writers/latex2e/__init__.py | 3295 ++ .../docutils/writers/latex2e/default.tex | 14 + .../docutils/writers/latex2e/docutils.sty | 223 + .../docutils/writers/latex2e/titlepage.tex | 20 + .../docutils/writers/latex2e/titlingpage.tex | 18 + .../docutils/writers/latex2e/xelatex.tex | 21 + .../site-packages/docutils/writers/manpage.py | 1181 + .../site-packages/docutils/writers/null.py | 21 + .../docutils/writers/odf_odt/__init__.py | 3468 +++ .../writers/odf_odt/pygmentsformatter.py | 109 + .../docutils/writers/odf_odt/styles.odt | Bin 0 -> 16500 bytes .../docutils/writers/pep_html/__init__.py | 103 + .../docutils/writers/pep_html/pep.css | 344 + .../docutils/writers/pep_html/template.txt | 25 + .../docutils/writers/pseudoxml.py | 40 + .../docutils/writers/s5_html/__init__.py | 352 + .../writers/s5_html/themes/README.txt | 6 + .../writers/s5_html/themes/big-black/__base__ | 2 + .../s5_html/themes/big-black/framing.css | 25 + .../s5_html/themes/big-black/pretty.css | 109 + .../s5_html/themes/big-white/framing.css | 24 + .../s5_html/themes/big-white/pretty.css | 107 + .../s5_html/themes/default/framing.css | 25 + .../writers/s5_html/themes/default/opera.css | 8 + .../s5_html/themes/default/outline.css | 16 + .../writers/s5_html/themes/default/pretty.css | 120 + .../writers/s5_html/themes/default/print.css | 24 + .../s5_html/themes/default/s5-core.css | 11 + .../writers/s5_html/themes/default/slides.css | 10 + .../writers/s5_html/themes/default/slides.js | 558 + .../s5_html/themes/medium-black/__base__ | 2 + .../s5_html/themes/medium-black/pretty.css | 115 + .../s5_html/themes/medium-white/framing.css | 24 + .../s5_html/themes/medium-white/pretty.css | 113 + .../s5_html/themes/small-black/__base__ | 2 + .../s5_html/themes/small-black/pretty.css | 116 + .../s5_html/themes/small-white/framing.css | 24 + .../s5_html/themes/small-white/pretty.css | 114 + .../docutils/writers/xetex/__init__.py | 149 + .../et_xmlfile-1.1.0.dist-info/AUTHORS.txt | 4 + .../et_xmlfile-1.1.0.dist-info/INSTALLER | 1 + .../et_xmlfile-1.1.0.dist-info/LICENCE.rst | 34 + .../et_xmlfile-1.1.0.dist-info/METADATA | 37 + .../et_xmlfile-1.1.0.dist-info/RECORD | 12 + .../et_xmlfile-1.1.0.dist-info/REQUESTED | 0 .../et_xmlfile-1.1.0.dist-info/WHEEL | 5 + .../et_xmlfile-1.1.0.dist-info/top_level.txt | 1 + .../site-packages/et_xmlfile/__init__.py | 11 + .../site-packages/et_xmlfile/xmlfile.py | 104 + .../exceptiongroup-1.1.0.dist-info/INSTALLER | 1 + .../exceptiongroup-1.1.0.dist-info/LICENSE | 73 + .../exceptiongroup-1.1.0.dist-info/METADATA | 141 + .../exceptiongroup-1.1.0.dist-info/RECORD | 17 + .../exceptiongroup-1.1.0.dist-info/REQUESTED | 0 .../exceptiongroup-1.1.0.dist-info/WHEEL | 4 + .../site-packages/exceptiongroup/__init__.py | 40 + .../site-packages/exceptiongroup/_catch.py | 114 + .../exceptiongroup/_exceptions.py | 280 + .../exceptiongroup/_formatting.py | 554 + .../site-packages/exceptiongroup/_version.py | 4 + .../site-packages/exceptiongroup/py.typed | 0 .../execnet-1.9.0.dist-info/INSTALLER | 1 + .../execnet-1.9.0.dist-info/LICENSE | 18 + .../execnet-1.9.0.dist-info/METADATA | 87 + .../execnet-1.9.0.dist-info/RECORD | 45 + .../execnet-1.9.0.dist-info/REQUESTED | 0 .../execnet-1.9.0.dist-info/WHEEL | 6 + .../execnet-1.9.0.dist-info/top_level.txt | 1 + .../site-packages/execnet/__init__.py | 51 + .../site-packages/execnet/_version.py | 5 + .../site-packages/execnet/deprecated.py | 49 + .../site-packages/execnet/gateway.py | 225 + .../site-packages/execnet/gateway_base.py | 1554 + .../execnet/gateway_bootstrap.py | 110 + .../site-packages/execnet/gateway_io.py | 250 + .../site-packages/execnet/gateway_socket.py | 95 + .../python3.11/site-packages/execnet/multi.py | 317 + .../python3.11/site-packages/execnet/rsync.py | 204 + .../site-packages/execnet/rsync_remote.py | 114 + .../site-packages/execnet/script/__init__.py | 2 + .../execnet/script/loop_socketserver.py | 15 + .../execnet/script/quitserver.py | 16 + .../site-packages/execnet/script/shell.py | 89 + .../execnet/script/socketserver.py | 140 + .../execnet/script/socketserverservice.py | 92 + .../site-packages/execnet/script/xx.py | 13 + .../python3.11/site-packages/execnet/xspec.py | 60 + .../idna-3.11.dist-info/INSTALLER | 1 + .../idna-3.11.dist-info/METADATA | 209 + .../site-packages/idna-3.11.dist-info/RECORD | 22 + .../site-packages/idna-3.11.dist-info/WHEEL | 4 + .../idna-3.11.dist-info/licenses/LICENSE.md | 31 + .../python3.11/site-packages/idna/__init__.py | 45 + .../python3.11/site-packages/idna/codec.py | 122 + .../python3.11/site-packages/idna/compat.py | 15 + .../lib/python3.11/site-packages/idna/core.py | 437 + .../python3.11/site-packages/idna/idnadata.py | 4309 +++ .../site-packages/idna/intranges.py | 57 + .../site-packages/idna/package_data.py | 1 + .../python3.11/site-packages/idna/py.typed | 0 .../site-packages/idna/uts46data.py | 8841 ++++++ .../INSTALLER | 1 + .../LICENSE | 202 + .../METADATA | 135 + .../importlib_metadata-6.0.0.dist-info/RECORD | 26 + .../REQUESTED | 0 .../importlib_metadata-6.0.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../importlib_metadata/__init__.py | 904 + .../importlib_metadata/_adapters.py | 90 + .../importlib_metadata/_collections.py | 30 + .../importlib_metadata/_compat.py | 72 + .../importlib_metadata/_functools.py | 104 + .../importlib_metadata/_itertools.py | 73 + .../site-packages/importlib_metadata/_meta.py | 49 + .../importlib_metadata/_py39compat.py | 35 + .../site-packages/importlib_metadata/_text.py | 99 + .../site-packages/importlib_metadata/py.typed | 0 .../iniconfig-2.0.0.dist-info/INSTALLER | 1 + .../iniconfig-2.0.0.dist-info/METADATA | 80 + .../iniconfig-2.0.0.dist-info/RECORD | 15 + .../iniconfig-2.0.0.dist-info/REQUESTED | 0 .../iniconfig-2.0.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 19 + .../site-packages/iniconfig/__init__.py | 216 + .../site-packages/iniconfig/_parse.py | 82 + .../site-packages/iniconfig/_version.py | 4 + .../site-packages/iniconfig/exceptions.py | 20 + .../site-packages/iniconfig/py.typed | 0 .../isort-6.0.1.dist-info/INSTALLER | 1 + .../isort-6.0.1.dist-info/METADATA | 370 + .../isort-6.0.1.dist-info/RECORD | 102 + .../isort-6.0.1.dist-info/REQUESTED | 0 .../site-packages/isort-6.0.1.dist-info/WHEEL | 4 + .../isort-6.0.1.dist-info/entry_points.txt | 9 + .../isort-6.0.1.dist-info/licenses/LICENSE | 21 + .../site-packages/isort/__init__.py | 39 + .../site-packages/isort/__main__.py | 3 + .../isort/_vendored/tomli/LICENSE | 21 + .../isort/_vendored/tomli/__init__.py | 6 + .../isort/_vendored/tomli/_parser.py | 650 + .../isort/_vendored/tomli/_re.py | 100 + .../isort/_vendored/tomli/py.typed | 1 + .../site-packages/isort/_version.py | 3 + .../lib/python3.11/site-packages/isort/api.py | 659 + .../site-packages/isort/comments.py | 32 + .../python3.11/site-packages/isort/core.py | 513 + .../isort/deprecated/__init__.py | 0 .../site-packages/isort/deprecated/finders.py | 393 + .../site-packages/isort/exceptions.py | 198 + .../python3.11/site-packages/isort/files.py | 41 + .../python3.11/site-packages/isort/format.py | 157 + .../python3.11/site-packages/isort/hooks.py | 94 + .../site-packages/isort/identify.py | 207 + .../lib/python3.11/site-packages/isort/io.py | 72 + .../python3.11/site-packages/isort/literal.py | 114 + .../python3.11/site-packages/isort/logo.py | 19 + .../python3.11/site-packages/isort/main.py | 1307 + .../python3.11/site-packages/isort/output.py | 676 + .../python3.11/site-packages/isort/parse.py | 603 + .../python3.11/site-packages/isort/place.py | 146 + .../site-packages/isort/profiles.py | 96 + .../python3.11/site-packages/isort/py.typed | 0 .../site-packages/isort/pylama_isort.py | 45 + .../site-packages/isort/sections.py | 10 + .../site-packages/isort/settings.py | 941 + .../isort/setuptools_commands.py | 62 + .../python3.11/site-packages/isort/sorting.py | 130 + .../site-packages/isort/stdlibs/__init__.py | 17 + .../site-packages/isort/stdlibs/all.py | 3 + .../site-packages/isort/stdlibs/py2.py | 3 + .../site-packages/isort/stdlibs/py27.py | 301 + .../site-packages/isort/stdlibs/py3.py | 12 + .../site-packages/isort/stdlibs/py310.py | 232 + .../site-packages/isort/stdlibs/py311.py | 232 + .../site-packages/isort/stdlibs/py312.py | 227 + .../site-packages/isort/stdlibs/py313.py | 207 + .../site-packages/isort/stdlibs/py36.py | 224 + .../site-packages/isort/stdlibs/py37.py | 225 + .../site-packages/isort/stdlibs/py38.py | 233 + .../site-packages/isort/stdlibs/py39.py | 234 + .../python3.11/site-packages/isort/utils.py | 74 + .../python3.11/site-packages/isort/wrap.py | 147 + .../site-packages/isort/wrap_modes.py | 374 + .../jaraco.classes-3.2.3.dist-info/INSTALLER | 1 + .../jaraco.classes-3.2.3.dist-info/LICENSE | 19 + .../jaraco.classes-3.2.3.dist-info/METADATA | 70 + .../jaraco.classes-3.2.3.dist-info/RECORD | 15 + .../jaraco.classes-3.2.3.dist-info/REQUESTED | 0 .../jaraco.classes-3.2.3.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/jaraco/classes/__init__.py | 0 .../site-packages/jaraco/classes/ancestry.py | 68 + .../site-packages/jaraco/classes/meta.py | 66 + .../jaraco/classes/properties.py | 170 + .../jeepney-0.8.0.dist-info/INSTALLER | 1 + .../jeepney-0.8.0.dist-info/LICENSE | 21 + .../jeepney-0.8.0.dist-info/METADATA | 36 + .../jeepney-0.8.0.dist-info/RECORD | 67 + .../jeepney-0.8.0.dist-info/REQUESTED | 0 .../jeepney-0.8.0.dist-info/WHEEL | 4 + .../site-packages/jeepney/__init__.py | 13 + .../python3.11/site-packages/jeepney/auth.py | 136 + .../site-packages/jeepney/bindgen.py | 126 + .../python3.11/site-packages/jeepney/bus.py | 62 + .../site-packages/jeepney/bus_messages.py | 235 + .../python3.11/site-packages/jeepney/fds.py | 158 + .../site-packages/jeepney/io/__init__.py | 1 + .../site-packages/jeepney/io/asyncio.py | 233 + .../site-packages/jeepney/io/blocking.py | 350 + .../site-packages/jeepney/io/common.py | 88 + .../jeepney/io/tests/__init__.py | 0 .../jeepney/io/tests/conftest.py | 81 + .../jeepney/io/tests/test_asyncio.py | 91 + .../jeepney/io/tests/test_blocking.py | 88 + .../jeepney/io/tests/test_threading.py | 83 + .../jeepney/io/tests/test_trio.py | 114 + .../site-packages/jeepney/io/tests/utils.py | 3 + .../site-packages/jeepney/io/threading.py | 273 + .../site-packages/jeepney/io/trio.py | 420 + .../site-packages/jeepney/low_level.py | 585 + .../site-packages/jeepney/routing.py | 76 + .../site-packages/jeepney/tests/__init__.py | 0 .../jeepney/tests/secrets_introspect.xml | 116 + .../site-packages/jeepney/tests/test_auth.py | 24 + .../jeepney/tests/test_bindgen.py | 28 + .../site-packages/jeepney/tests/test_bus.py | 24 + .../jeepney/tests/test_bus_messages.py | 109 + .../site-packages/jeepney/tests/test_fds.py | 80 + .../jeepney/tests/test_low_level.py | 87 + .../jeepney/tests/test_routing.py | 32 + .../site-packages/jeepney/wrappers.py | 216 + .../site-packages/jinja2/__init__.py | 37 + .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 84 + .../site-packages/jinja2/bccache.py | 406 + .../site-packages/jinja2/compiler.py | 1957 ++ .../site-packages/jinja2/constants.py | 20 + .../python3.11/site-packages/jinja2/debug.py | 191 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1667 ++ .../site-packages/jinja2/exceptions.py | 166 + .../python3.11/site-packages/jinja2/ext.py | 859 + .../site-packages/jinja2/filters.py | 1840 ++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.11/site-packages/jinja2/lexer.py | 866 + .../site-packages/jinja2/loaders.py | 661 + .../python3.11/site-packages/jinja2/meta.py | 111 + .../site-packages/jinja2/nativetypes.py | 130 + .../python3.11/site-packages/jinja2/nodes.py | 1204 + .../site-packages/jinja2/optimizer.py | 47 + .../python3.11/site-packages/jinja2/parser.py | 1032 + .../python3.11/site-packages/jinja2/py.typed | 0 .../site-packages/jinja2/runtime.py | 1053 + .../site-packages/jinja2/sandbox.py | 428 + .../python3.11/site-packages/jinja2/tests.py | 255 + .../python3.11/site-packages/jinja2/utils.py | 755 + .../site-packages/jinja2/visitor.py | 92 + .../keyring-23.13.1.dist-info/INSTALLER | 1 + .../keyring-23.13.1.dist-info/LICENSE | 19 + .../keyring-23.13.1.dist-info/METADATA | 541 + .../keyring-23.13.1.dist-info/RECORD | 71 + .../keyring-23.13.1.dist-info/REQUESTED | 0 .../keyring-23.13.1.dist-info/WHEEL | 5 + .../entry_points.txt | 13 + .../keyring-23.13.1.dist-info/top_level.txt | 1 + .../site-packages/keyring/__init__.py | 17 + .../site-packages/keyring/__main__.py | 4 + .../site-packages/keyring/_compat.py | 7 + .../keyring/_properties_compat.py | 169 + .../site-packages/keyring/backend.py | 256 + .../keyring/backend_complete.zsh | 14 + .../site-packages/keyring/backends/OS_X.py | 13 + .../keyring/backends/SecretService.py | 119 + .../site-packages/keyring/backends/Windows.py | 170 + .../keyring/backends/__init__.py | 0 .../site-packages/keyring/backends/chainer.py | 71 + .../site-packages/keyring/backends/fail.py | 27 + .../site-packages/keyring/backends/kwallet.py | 165 + .../keyring/backends/libsecret.py | 156 + .../keyring/backends/macOS/__init__.py | 76 + .../keyring/backends/macOS/api.py | 173 + .../site-packages/keyring/backends/null.py | 17 + .../python3.11/site-packages/keyring/cli.py | 138 + .../site-packages/keyring/completion.py | 51 + .../python3.11/site-packages/keyring/core.py | 186 + .../site-packages/keyring/credentials.py | 70 + .../site-packages/keyring/devpi_client.py | 34 + .../site-packages/keyring/errors.py | 61 + .../python3.11/site-packages/keyring/http.py | 39 + .../python3.11/site-packages/keyring/py.typed | 0 .../site-packages/keyring/py312compat.py | 10 + .../site-packages/keyring/testing/__init__.py | 0 .../site-packages/keyring/testing/backend.py | 178 + .../site-packages/keyring/testing/util.py | 71 + .../site-packages/keyring/util/__init__.py | 37 + .../site-packages/keyring/util/platform_.py | 68 + .../site-packages/keyring/util/properties.py | 18 + .../AUTHORS.rst | 15 + .../CHANGELOG.rst | 87 + .../CODE_OF_CONDUCT.rst | 86 + .../INSTALLER | 1 + .../METADATA | 296 + .../NOTICE | 19 + .../RECORD | 19 + .../REQUESTED | 0 .../license_expression-30.1.0.dist-info/WHEEL | 5 + .../apache-2.0.LICENSE | 201 + .../top_level.txt | 1 + .../license_expression/__init__.py | 1834 ++ .../license_expression/_pyahocorasick.ABOUT | 23 + .../license_expression/_pyahocorasick.py | 624 + .../license_expression/data/cc-by-4.0.LICENSE | 395 + .../data/license_key_index.json.ABOUT | 7 + .../data/scancode-licensedb-index.json | 24860 ++++++++++++++++ .../site-packages/markdown_it/__init__.py | 5 + .../site-packages/markdown_it/_compat.py | 10 + .../site-packages/markdown_it/_punycode.py | 66 + .../site-packages/markdown_it/cli/__init__.py | 0 .../site-packages/markdown_it/cli/parse.py | 109 + .../markdown_it/common/__init__.py | 0 .../markdown_it/common/entities.py | 4 + .../markdown_it/common/html_blocks.py | 68 + .../markdown_it/common/html_re.py | 40 + .../markdown_it/common/normalize_url.py | 82 + .../site-packages/markdown_it/common/utils.py | 333 + .../markdown_it/helpers/__init__.py | 6 + .../helpers/parse_link_destination.py | 86 + .../markdown_it/helpers/parse_link_label.py | 43 + .../markdown_it/helpers/parse_link_title.py | 60 + .../site-packages/markdown_it/main.py | 331 + .../site-packages/markdown_it/parser_block.py | 109 + .../site-packages/markdown_it/parser_core.py | 32 + .../markdown_it/parser_inline.py | 124 + .../site-packages/markdown_it/port.yaml | 49 + .../markdown_it/presets/__init__.py | 27 + .../markdown_it/presets/commonmark.py | 73 + .../markdown_it/presets/default.py | 34 + .../site-packages/markdown_it/presets/zero.py | 39 + .../site-packages/markdown_it/py.typed | 1 + .../site-packages/markdown_it/renderer.py | 334 + .../site-packages/markdown_it/ruler.py | 237 + .../markdown_it/rules_block/__init__.py | 27 + .../markdown_it/rules_block/blockquote.py | 297 + .../markdown_it/rules_block/code.py | 35 + .../markdown_it/rules_block/fence.py | 103 + .../markdown_it/rules_block/heading.py | 71 + .../markdown_it/rules_block/hr.py | 53 + .../markdown_it/rules_block/html_block.py | 91 + .../markdown_it/rules_block/lheading.py | 89 + .../markdown_it/rules_block/list.py | 340 + .../markdown_it/rules_block/paragraph.py | 66 + .../markdown_it/rules_block/reference.py | 217 + .../markdown_it/rules_block/state_block.py | 229 + .../markdown_it/rules_block/table.py | 238 + .../markdown_it/rules_core/__init__.py | 17 + .../markdown_it/rules_core/block.py | 15 + .../markdown_it/rules_core/inline.py | 10 + .../markdown_it/rules_core/linkify.py | 141 + .../markdown_it/rules_core/normalize.py | 18 + .../markdown_it/rules_core/replacements.py | 126 + .../markdown_it/rules_core/smartquotes.py | 201 + .../markdown_it/rules_core/state_core.py | 25 + .../markdown_it/rules_inline/__init__.py | 29 + .../markdown_it/rules_inline/autolink.py | 77 + .../markdown_it/rules_inline/backticks.py | 74 + .../markdown_it/rules_inline/balance_pairs.py | 112 + .../markdown_it/rules_inline/emphasis.py | 101 + .../markdown_it/rules_inline/entity.py | 53 + .../markdown_it/rules_inline/escape.py | 49 + .../markdown_it/rules_inline/html_inline.py | 42 + .../markdown_it/rules_inline/image.py | 150 + .../markdown_it/rules_inline/link.py | 149 + .../markdown_it/rules_inline/newline.py | 43 + .../markdown_it/rules_inline/state_inline.py | 175 + .../markdown_it/rules_inline/strikethrough.py | 131 + .../markdown_it/rules_inline/text.py | 57 + .../markdown_it/rules_inline/text_collapse.py | 43 + .../site-packages/markdown_it/token.py | 180 + .../site-packages/markdown_it/tree.py | 330 + .../site-packages/markdown_it/utils.py | 122 + .../markdown_it_py-2.2.0.dist-info/INSTALLER | 1 + .../markdown_it_py-2.2.0.dist-info/LICENSE | 21 + .../LICENSE.markdown-it | 22 + .../markdown_it_py-2.2.0.dist-info/METADATA | 204 + .../markdown_it_py-2.2.0.dist-info/RECORD | 139 + .../markdown_it_py-2.2.0.dist-info/REQUESTED | 0 .../markdown_it_py-2.2.0.dist-info/WHEEL | 4 + .../entry_points.txt | 3 + .../site-packages/markupsafe/__init__.py | 295 + .../site-packages/markupsafe/_native.py | 63 + .../site-packages/markupsafe/_speedups.c | 320 + .../_speedups.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 53656 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../mdurl-0.1.2.dist-info/INSTALLER | 1 + .../mdurl-0.1.2.dist-info/LICENSE | 46 + .../mdurl-0.1.2.dist-info/METADATA | 32 + .../mdurl-0.1.2.dist-info/RECORD | 19 + .../mdurl-0.1.2.dist-info/REQUESTED | 0 .../site-packages/mdurl-0.1.2.dist-info/WHEEL | 4 + .../site-packages/mdurl/__init__.py | 18 + .../python3.11/site-packages/mdurl/_decode.py | 104 + .../python3.11/site-packages/mdurl/_encode.py | 85 + .../python3.11/site-packages/mdurl/_format.py | 27 + .../python3.11/site-packages/mdurl/_parse.py | 304 + .../python3.11/site-packages/mdurl/_url.py | 14 + .../python3.11/site-packages/mdurl/py.typed | 1 + .../more_itertools-9.0.0.dist-info/INSTALLER | 1 + .../more_itertools-9.0.0.dist-info/LICENSE | 19 + .../more_itertools-9.0.0.dist-info/METADATA | 241 + .../more_itertools-9.0.0.dist-info/RECORD | 16 + .../more_itertools-9.0.0.dist-info/REQUESTED | 0 .../more_itertools-9.0.0.dist-info/WHEEL | 4 + .../site-packages/more_itertools/__init__.py | 6 + .../site-packages/more_itertools/__init__.pyi | 2 + .../site-packages/more_itertools/more.py | 4347 +++ .../site-packages/more_itertools/more.pyi | 674 + .../site-packages/more_itertools/py.typed | 0 .../site-packages/more_itertools/recipes.py | 841 + .../site-packages/more_itertools/recipes.pyi | 110 + .../mypy_extensions-1.0.0.dist-info/INSTALLER | 1 + .../mypy_extensions-1.0.0.dist-info/LICENSE | 27 + .../mypy_extensions-1.0.0.dist-info/METADATA | 29 + .../mypy_extensions-1.0.0.dist-info/RECORD | 9 + .../mypy_extensions-1.0.0.dist-info/REQUESTED | 0 .../mypy_extensions-1.0.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/mypy_extensions.py | 213 + .../openpyxl-3.1.1.dist-info/INSTALLER | 1 + .../openpyxl-3.1.1.dist-info/LICENCE.rst | 23 + .../openpyxl-3.1.1.dist-info/METADATA | 86 + .../openpyxl-3.1.1.dist-info/RECORD | 385 + .../openpyxl-3.1.1.dist-info/REQUESTED | 0 .../openpyxl-3.1.1.dist-info/WHEEL | 6 + .../openpyxl-3.1.1.dist-info/top_level.txt | 1 + .../site-packages/openpyxl/__init__.py | 19 + .../site-packages/openpyxl/_constants.py | 13 + .../site-packages/openpyxl/cell/__init__.py | 4 + .../site-packages/openpyxl/cell/_writer.py | 158 + .../site-packages/openpyxl/cell/cell.py | 332 + .../site-packages/openpyxl/cell/read_only.py | 136 + .../site-packages/openpyxl/cell/rich_text.py | 167 + .../site-packages/openpyxl/cell/text.py | 184 + .../site-packages/openpyxl/chart/_3d.py | 105 + .../site-packages/openpyxl/chart/__init__.py | 19 + .../site-packages/openpyxl/chart/_chart.py | 199 + .../openpyxl/chart/area_chart.py | 106 + .../site-packages/openpyxl/chart/axis.py | 401 + .../site-packages/openpyxl/chart/bar_chart.py | 144 + .../openpyxl/chart/bubble_chart.py | 67 + .../openpyxl/chart/chartspace.py | 195 + .../openpyxl/chart/data_source.py | 246 + .../openpyxl/chart/descriptors.py | 43 + .../site-packages/openpyxl/chart/error_bar.py | 62 + .../site-packages/openpyxl/chart/label.py | 127 + .../site-packages/openpyxl/chart/layout.py | 74 + .../site-packages/openpyxl/chart/legend.py | 75 + .../openpyxl/chart/line_chart.py | 129 + .../site-packages/openpyxl/chart/marker.py | 90 + .../site-packages/openpyxl/chart/picture.py | 35 + .../site-packages/openpyxl/chart/pie_chart.py | 177 + .../site-packages/openpyxl/chart/pivot.py | 65 + .../site-packages/openpyxl/chart/plotarea.py | 162 + .../openpyxl/chart/print_settings.py | 57 + .../openpyxl/chart/radar_chart.py | 55 + .../site-packages/openpyxl/chart/reader.py | 32 + .../site-packages/openpyxl/chart/reference.py | 124 + .../openpyxl/chart/scatter_chart.py | 53 + .../site-packages/openpyxl/chart/series.py | 197 + .../openpyxl/chart/series_factory.py | 41 + .../site-packages/openpyxl/chart/shapes.py | 89 + .../openpyxl/chart/stock_chart.py | 54 + .../openpyxl/chart/surface_chart.py | 119 + .../site-packages/openpyxl/chart/text.py | 78 + .../site-packages/openpyxl/chart/title.py | 76 + .../site-packages/openpyxl/chart/trendline.py | 98 + .../openpyxl/chart/updown_bars.py | 31 + .../openpyxl/chartsheet/__init__.py | 3 + .../openpyxl/chartsheet/chartsheet.py | 107 + .../openpyxl/chartsheet/custom.py | 61 + .../openpyxl/chartsheet/properties.py | 28 + .../openpyxl/chartsheet/protection.py | 41 + .../openpyxl/chartsheet/publish.py | 58 + .../openpyxl/chartsheet/relation.py | 97 + .../openpyxl/chartsheet/views.py | 51 + .../openpyxl/comments/__init__.py | 4 + .../site-packages/openpyxl/comments/author.py | 21 + .../openpyxl/comments/comment_sheet.py | 211 + .../openpyxl/comments/comments.py | 62 + .../openpyxl/comments/shape_writer.py | 112 + .../site-packages/openpyxl/compat/__init__.py | 54 + .../site-packages/openpyxl/compat/abc.py | 8 + .../site-packages/openpyxl/compat/numbers.py | 43 + .../site-packages/openpyxl/compat/product.py | 17 + .../openpyxl/compat/singleton.py | 40 + .../site-packages/openpyxl/compat/strings.py | 25 + .../openpyxl/descriptors/__init__.py | 58 + .../openpyxl/descriptors/base.py | 272 + .../openpyxl/descriptors/excel.py | 112 + .../openpyxl/descriptors/namespace.py | 12 + .../openpyxl/descriptors/nested.py | 131 + .../openpyxl/descriptors/sequence.py | 136 + .../openpyxl/descriptors/serialisable.py | 240 + .../openpyxl/descriptors/slots.py | 18 + .../openpyxl/drawing/__init__.py | 4 + .../site-packages/openpyxl/drawing/colors.py | 435 + .../openpyxl/drawing/connector.py | 144 + .../site-packages/openpyxl/drawing/drawing.py | 92 + .../site-packages/openpyxl/drawing/effect.py | 407 + .../site-packages/openpyxl/drawing/fill.py | 425 + .../openpyxl/drawing/geometry.py | 584 + .../site-packages/openpyxl/drawing/graphic.py | 177 + .../site-packages/openpyxl/drawing/image.py | 65 + .../site-packages/openpyxl/drawing/line.py | 144 + .../site-packages/openpyxl/drawing/picture.py | 144 + .../openpyxl/drawing/properties.py | 174 + .../openpyxl/drawing/relation.py | 17 + .../openpyxl/drawing/spreadsheet_drawing.py | 381 + .../site-packages/openpyxl/drawing/text.py | 717 + .../site-packages/openpyxl/drawing/xdr.py | 33 + .../openpyxl/formatting/__init__.py | 3 + .../openpyxl/formatting/formatting.py | 114 + .../site-packages/openpyxl/formatting/rule.py | 291 + .../openpyxl/formula/__init__.py | 3 + .../openpyxl/formula/tokenizer.py | 446 + .../openpyxl/formula/translate.py | 166 + .../openpyxl/packaging/__init__.py | 3 + .../site-packages/openpyxl/packaging/core.py | 114 + .../openpyxl/packaging/custom.py | 277 + .../openpyxl/packaging/extended.py | 141 + .../openpyxl/packaging/interface.py | 56 + .../openpyxl/packaging/manifest.py | 194 + .../openpyxl/packaging/relationship.py | 171 + .../openpyxl/packaging/workbook.py | 185 + .../site-packages/openpyxl/pivot/__init__.py | 1 + .../site-packages/openpyxl/pivot/cache.py | 1119 + .../site-packages/openpyxl/pivot/fields.py | 322 + .../site-packages/openpyxl/pivot/record.py | 111 + .../site-packages/openpyxl/pivot/table.py | 1252 + .../site-packages/openpyxl/reader/__init__.py | 1 + .../site-packages/openpyxl/reader/drawings.py | 67 + .../site-packages/openpyxl/reader/excel.py | 347 + .../site-packages/openpyxl/reader/strings.py | 44 + .../site-packages/openpyxl/reader/workbook.py | 134 + .../site-packages/openpyxl/styles/__init__.py | 11 + .../openpyxl/styles/alignment.py | 72 + .../site-packages/openpyxl/styles/borders.py | 113 + .../site-packages/openpyxl/styles/builtins.py | 1397 + .../openpyxl/styles/cell_style.py | 202 + .../site-packages/openpyxl/styles/colors.py | 172 + .../openpyxl/styles/differential.py | 95 + .../site-packages/openpyxl/styles/fills.py | 224 + .../site-packages/openpyxl/styles/fonts.py | 113 + .../openpyxl/styles/named_styles.py | 291 + .../site-packages/openpyxl/styles/numbers.py | 200 + .../openpyxl/styles/protection.py | 17 + .../site-packages/openpyxl/styles/proxy.py | 62 + .../openpyxl/styles/styleable.py | 152 + .../openpyxl/styles/stylesheet.py | 263 + .../site-packages/openpyxl/styles/table.py | 94 + .../site-packages/openpyxl/utils/__init__.py | 17 + .../openpyxl/utils/bound_dictionary.py | 26 + .../site-packages/openpyxl/utils/cell.py | 227 + .../site-packages/openpyxl/utils/dataframe.py | 87 + .../site-packages/openpyxl/utils/datetime.py | 140 + .../site-packages/openpyxl/utils/escape.py | 43 + .../openpyxl/utils/exceptions.py | 34 + .../site-packages/openpyxl/utils/formulas.py | 24 + .../openpyxl/utils/indexed_list.py | 49 + .../site-packages/openpyxl/utils/inference.py | 60 + .../openpyxl/utils/protection.py | 22 + .../site-packages/openpyxl/utils/units.py | 108 + .../openpyxl/workbook/__init__.py | 4 + .../openpyxl/workbook/_writer.py | 197 + .../site-packages/openpyxl/workbook/child.py | 166 + .../openpyxl/workbook/defined_name.py | 189 + .../workbook/external_link/__init__.py | 3 + .../workbook/external_link/external.py | 190 + .../openpyxl/workbook/external_reference.py | 18 + .../openpyxl/workbook/function_group.py | 36 + .../openpyxl/workbook/properties.py | 151 + .../openpyxl/workbook/protection.py | 163 + .../openpyxl/workbook/smart_tags.py | 56 + .../site-packages/openpyxl/workbook/views.py | 155 + .../site-packages/openpyxl/workbook/web.py | 98 + .../openpyxl/workbook/workbook.py | 438 + .../openpyxl/worksheet/__init__.py | 1 + .../openpyxl/worksheet/_read_only.py | 186 + .../openpyxl/worksheet/_reader.py | 472 + .../openpyxl/worksheet/_write_only.py | 160 + .../openpyxl/worksheet/_writer.py | 390 + .../openpyxl/worksheet/cell_range.py | 512 + .../openpyxl/worksheet/cell_watch.py | 34 + .../openpyxl/worksheet/controls.py | 107 + .../openpyxl/worksheet/copier.py | 70 + .../openpyxl/worksheet/custom.py | 35 + .../openpyxl/worksheet/datavalidation.py | 202 + .../openpyxl/worksheet/dimensions.py | 296 + .../openpyxl/worksheet/drawing.py | 14 + .../openpyxl/worksheet/errors.py | 93 + .../openpyxl/worksheet/filters.py | 387 + .../openpyxl/worksheet/formula.py | 51 + .../openpyxl/worksheet/header_footer.py | 270 + .../openpyxl/worksheet/hyperlink.py | 61 + .../site-packages/openpyxl/worksheet/merge.py | 141 + .../site-packages/openpyxl/worksheet/ole.py | 133 + .../site-packages/openpyxl/worksheet/page.py | 174 + .../openpyxl/worksheet/pagebreak.py | 94 + .../openpyxl/worksheet/picture.py | 8 + .../openpyxl/worksheet/print_settings.py | 185 + .../openpyxl/worksheet/properties.py | 97 + .../openpyxl/worksheet/protection.py | 120 + .../openpyxl/worksheet/related.py | 17 + .../openpyxl/worksheet/scenario.py | 105 + .../openpyxl/worksheet/smart_tag.py | 78 + .../site-packages/openpyxl/worksheet/table.py | 385 + .../site-packages/openpyxl/worksheet/views.py | 149 + .../openpyxl/worksheet/worksheet.py | 904 + .../site-packages/openpyxl/writer/__init__.py | 1 + .../site-packages/openpyxl/writer/excel.py | 295 + .../site-packages/openpyxl/writer/theme.py | 291 + .../site-packages/openpyxl/xml/__init__.py | 42 + .../site-packages/openpyxl/xml/constants.py | 129 + .../site-packages/openpyxl/xml/functions.py | 87 + .../site-packages/packageurl/__init__.py | 407 + .../packageurl/contrib/__init__.py | 0 .../packageurl/contrib/django/__init__.py | 0 .../packageurl/contrib/django/filters.py | 61 + .../packageurl/contrib/django/models.py | 176 + .../packageurl/contrib/django/utils.py | 57 + .../packageurl/contrib/purl2url.py | 336 + .../site-packages/packageurl/contrib/route.py | 224 + .../packageurl/contrib/url2purl.py | 641 + .../AUTHORS.rst | 12 + .../CHANGELOG.rst | 143 + .../CONTRIBUTING.rst | 28 + .../INSTALLER | 1 + .../METADATA | 157 + .../README.rst | 128 + .../packageurl_python-0.10.4.dist-info/RECORD | 29 + .../REQUESTED | 0 .../packageurl_python-0.10.4.dist-info/WHEEL | 5 + .../mit.LICENSE | 18 + .../top_level.txt | 1 + .../packaging-23.0.dist-info/INSTALLER | 1 + .../packaging-23.0.dist-info/LICENSE | 3 + .../packaging-23.0.dist-info/LICENSE.APACHE | 177 + .../packaging-23.0.dist-info/LICENSE.BSD | 23 + .../packaging-23.0.dist-info/METADATA | 98 + .../packaging-23.0.dist-info/RECORD | 35 + .../packaging-23.0.dist-info/REQUESTED | 0 .../packaging-23.0.dist-info/WHEEL | 4 + .../site-packages/packaging/__init__.py | 15 + .../site-packages/packaging/_elffile.py | 108 + .../site-packages/packaging/_manylinux.py | 238 + .../site-packages/packaging/_musllinux.py | 80 + .../site-packages/packaging/_parser.py | 328 + .../site-packages/packaging/_structures.py | 61 + .../site-packages/packaging/_tokenizer.py | 188 + .../site-packages/packaging/markers.py | 245 + .../site-packages/packaging/py.typed | 0 .../site-packages/packaging/requirements.py | 95 + .../site-packages/packaging/specifiers.py | 1005 + .../site-packages/packaging/tags.py | 546 + .../site-packages/packaging/utils.py | 141 + .../site-packages/packaging/version.py | 563 + .../pathspec-0.11.0.dist-info/INSTALLER | 1 + .../pathspec-0.11.0.dist-info/LICENSE | 373 + .../pathspec-0.11.0.dist-info/METADATA | 565 + .../pathspec-0.11.0.dist-info/RECORD | 23 + .../pathspec-0.11.0.dist-info/REQUESTED | 0 .../pathspec-0.11.0.dist-info/WHEEL | 4 + .../site-packages/pathspec/__init__.py | 76 + .../site-packages/pathspec/_meta.py | 54 + .../site-packages/pathspec/gitignore.py | 135 + .../site-packages/pathspec/pathspec.py | 270 + .../site-packages/pathspec/pattern.py | 206 + .../pathspec/patterns/__init__.py | 11 + .../pathspec/patterns/gitwildmatch.py | 410 + .../site-packages/pathspec/py.typed | 1 + .../python3.11/site-packages/pathspec/util.py | 713 + .../pip-24.0.dist-info/AUTHORS.txt | 760 + .../pip-24.0.dist-info/INSTALLER | 1 + .../pip-24.0.dist-info/LICENSE.txt | 20 + .../site-packages/pip-24.0.dist-info/METADATA | 88 + .../site-packages/pip-24.0.dist-info/RECORD | 1024 + .../pip-24.0.dist-info/REQUESTED | 0 .../site-packages/pip-24.0.dist-info/WHEEL | 5 + .../pip-24.0.dist-info/entry_points.txt | 4 + .../pip-24.0.dist-info/top_level.txt | 1 + .../python3.11/site-packages/pip/__init__.py | 13 + .../python3.11/site-packages/pip/__main__.py | 24 + .../site-packages/pip/__pip-runner__.py | 50 + .../site-packages/pip/_internal/__init__.py | 18 + .../site-packages/pip/_internal/build_env.py | 311 + .../site-packages/pip/_internal/cache.py | 290 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 172 + .../pip/_internal/cli/base_command.py | 236 + .../pip/_internal/cli/cmdoptions.py | 1074 + .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 79 + .../pip/_internal/cli/main_parser.py | 134 + .../site-packages/pip/_internal/cli/parser.py | 294 + .../pip/_internal/cli/progress_bars.py | 68 + .../pip/_internal/cli/req_command.py | 505 + .../pip/_internal/cli/spinners.py | 159 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 132 + .../pip/_internal/commands/cache.py | 225 + .../pip/_internal/commands/check.py | 54 + .../pip/_internal/commands/completion.py | 130 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 201 + .../pip/_internal/commands/download.py | 147 + .../pip/_internal/commands/freeze.py | 108 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 774 + .../pip/_internal/commands/list.py | 368 + .../pip/_internal/commands/search.py | 174 + .../pip/_internal/commands/show.py | 189 + .../pip/_internal/commands/uninstall.py | 113 + .../pip/_internal/commands/wheel.py | 183 + .../pip/_internal/configuration.py | 383 + .../pip/_internal/distributions/__init__.py | 21 + .../pip/_internal/distributions/base.py | 51 + .../pip/_internal/distributions/installed.py | 29 + .../pip/_internal/distributions/sdist.py | 156 + .../pip/_internal/distributions/wheel.py | 40 + .../site-packages/pip/_internal/exceptions.py | 728 + .../pip/_internal/index/__init__.py | 2 + .../pip/_internal/index/collector.py | 507 + .../pip/_internal/index/package_finder.py | 1027 + .../pip/_internal/index/sources.py | 285 + .../pip/_internal/locations/__init__.py | 467 + .../pip/_internal/locations/_distutils.py | 172 + .../pip/_internal/locations/_sysconfig.py | 213 + .../pip/_internal/locations/base.py | 81 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 128 + .../pip/_internal/metadata/_json.py | 84 + .../pip/_internal/metadata/base.py | 702 + .../_internal/metadata/importlib/__init__.py | 6 + .../_internal/metadata/importlib/_compat.py | 55 + .../_internal/metadata/importlib/_dists.py | 227 + .../pip/_internal/metadata/importlib/_envs.py | 189 + .../pip/_internal/metadata/pkg_resources.py | 278 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 30 + .../pip/_internal/models/direct_url.py | 235 + .../pip/_internal/models/format_control.py | 78 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 56 + .../pip/_internal/models/link.py | 579 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 132 + .../pip/_internal/models/selection_prefs.py | 51 + .../pip/_internal/models/target_python.py | 122 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../pip/_internal/network/auth.py | 561 + .../pip/_internal/network/cache.py | 106 + .../pip/_internal/network/download.py | 186 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 520 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 62 + .../pip/_internal/operations/__init__.py | 0 .../_internal/operations/build/__init__.py | 0 .../operations/build/build_tracker.py | 139 + .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 41 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 37 + .../operations/build/wheel_editable.py | 46 + .../operations/build/wheel_legacy.py | 102 + .../pip/_internal/operations/check.py | 187 + .../pip/_internal/operations/freeze.py | 255 + .../_internal/operations/install/__init__.py | 2 + .../operations/install/editable_legacy.py | 46 + .../pip/_internal/operations/install/wheel.py | 734 + .../pip/_internal/operations/prepare.py | 730 + .../site-packages/pip/_internal/pyproject.py | 179 + .../pip/_internal/req/__init__.py | 92 + .../pip/_internal/req/constructors.py | 576 + .../pip/_internal/req/req_file.py | 554 + .../pip/_internal/req/req_install.py | 923 + .../pip/_internal/req/req_set.py | 119 + .../pip/_internal/req/req_uninstall.py | 649 + .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 598 + .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 141 + .../resolution/resolvelib/candidates.py | 597 + .../resolution/resolvelib/factory.py | 812 + .../resolution/resolvelib/found_candidates.py | 155 + .../resolution/resolvelib/provider.py | 255 + .../resolution/resolvelib/reporter.py | 80 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 317 + .../pip/_internal/self_outdated_check.py | 248 + .../pip/_internal/utils/__init__.py | 0 .../pip/_internal/utils/_jaraco_text.py | 109 + .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 165 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 120 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/egg_link.py | 80 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 84 + .../pip/_internal/utils/filesystem.py | 153 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 88 + .../pip/_internal/utils/hashes.py | 151 + .../pip/_internal/utils/logging.py | 348 + .../site-packages/pip/_internal/utils/misc.py | 783 + .../pip/_internal/utils/models.py | 39 + .../pip/_internal/utils/packaging.py | 57 + .../pip/_internal/utils/setuptools_build.py | 146 + .../pip/_internal/utils/subprocess.py | 260 + .../pip/_internal/utils/temp_dir.py | 296 + .../pip/_internal/utils/unpacking.py | 257 + .../site-packages/pip/_internal/utils/urls.py | 62 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 134 + .../pip/_internal/vcs/__init__.py | 15 + .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 526 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 705 + .../pip/_internal/wheel_builder.py | 354 + .../site-packages/pip/_vendor/__init__.py | 121 + .../pip/_vendor/cachecontrol/__init__.py | 28 + .../pip/_vendor/cachecontrol/_cmd.py | 70 + .../pip/_vendor/cachecontrol/adapter.py | 161 + .../pip/_vendor/cachecontrol/cache.py | 74 + .../_vendor/cachecontrol/caches/__init__.py | 8 + .../_vendor/cachecontrol/caches/file_cache.py | 181 + .../cachecontrol/caches/redis_cache.py | 48 + .../pip/_vendor/cachecontrol/controller.py | 494 + .../pip/_vendor/cachecontrol/filewrapper.py | 119 + .../pip/_vendor/cachecontrol/heuristics.py | 154 + .../pip/_vendor/cachecontrol/py.typed | 0 .../pip/_vendor/cachecontrol/serialize.py | 206 + .../pip/_vendor/cachecontrol/wrapper.py | 43 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../pip/_vendor/certifi/cacert.pem | 4635 +++ .../site-packages/pip/_vendor/certifi/core.py | 108 + .../pip/_vendor/certifi/py.typed | 0 .../pip/_vendor/chardet/__init__.py | 115 + .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 261 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 147 + .../pip/_vendor/chardet/cli/__init__.py | 0 .../pip/_vendor/chardet/cli/chardetect.py | 112 + .../pip/_vendor/chardet/codingstatemachine.py | 90 + .../_vendor/chardet/codingstatemachinedict.py | 19 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 85 + .../pip/_vendor/chardet/escprober.py | 102 + .../pip/_vendor/chardet/escsm.py | 261 + .../pip/_vendor/chardet/eucjpprober.py | 102 + .../pip/_vendor/chardet/euckrfreq.py | 196 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 388 + .../pip/_vendor/chardet/euctwprober.py | 47 + .../pip/_vendor/chardet/gb2312freq.py | 284 + .../pip/_vendor/chardet/gb2312prober.py | 47 + .../pip/_vendor/chardet/hebrewprober.py | 316 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/johabfreq.py | 2382 ++ .../pip/_vendor/chardet/johabprober.py | 47 + .../pip/_vendor/chardet/jpcntx.py | 238 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4649 +++ .../pip/_vendor/chardet/langgreekmodel.py | 4397 +++ .../pip/_vendor/chardet/langhebrewmodel.py | 4380 +++ .../pip/_vendor/chardet/langhungarianmodel.py | 4649 +++ .../pip/_vendor/chardet/langrussianmodel.py | 5725 ++++ .../pip/_vendor/chardet/langthaimodel.py | 4380 +++ .../pip/_vendor/chardet/langturkishmodel.py | 4380 +++ .../pip/_vendor/chardet/latin1prober.py | 147 + .../pip/_vendor/chardet/macromanprober.py | 162 + .../pip/_vendor/chardet/mbcharsetprober.py | 95 + .../pip/_vendor/chardet/mbcsgroupprober.py | 57 + .../pip/_vendor/chardet/mbcssm.py | 661 + .../pip/_vendor/chardet/metadata/__init__.py | 0 .../pip/_vendor/chardet/metadata/languages.py | 352 + .../pip/_vendor/chardet/py.typed | 0 .../pip/_vendor/chardet/resultdict.py | 16 + .../pip/_vendor/chardet/sbcharsetprober.py | 162 + .../pip/_vendor/chardet/sbcsgroupprober.py | 88 + .../pip/_vendor/chardet/sjisprober.py | 105 + .../pip/_vendor/chardet/universaldetector.py | 362 + .../pip/_vendor/chardet/utf1632prober.py | 225 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 7 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 277 + .../pip/_vendor/colorama/initialise.py | 121 + .../pip/_vendor/colorama/tests/__init__.py | 1 + .../pip/_vendor/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../_vendor/colorama/tests/initialise_test.py | 189 + .../pip/_vendor/colorama/tests/isatty_test.py | 57 + .../pip/_vendor/colorama/tests/utils.py | 49 + .../_vendor/colorama/tests/winterm_test.py | 131 + .../pip/_vendor/colorama/win32.py | 180 + .../pip/_vendor/colorama/winterm.py | 195 + .../pip/_vendor/distlib/__init__.py | 33 + .../pip/_vendor/distlib/compat.py | 1138 + .../pip/_vendor/distlib/database.py | 1359 + .../pip/_vendor/distlib/index.py | 508 + .../pip/_vendor/distlib/locators.py | 1303 + .../pip/_vendor/distlib/manifest.py | 384 + .../pip/_vendor/distlib/markers.py | 167 + .../pip/_vendor/distlib/metadata.py | 1068 + .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 452 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 0 -> 182784 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 108032 bytes .../site-packages/pip/_vendor/distlib/util.py | 2025 ++ .../pip/_vendor/distlib/version.py | 751 + .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 91648 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 0 -> 168448 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 101888 bytes .../pip/_vendor/distlib/wheel.py | 1099 + .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../pip/_vendor/distro/distro.py | 1399 + .../site-packages/pip/_vendor/distro/py.typed | 0 .../pip/_vendor/idna/__init__.py | 44 + .../site-packages/pip/_vendor/idna/codec.py | 112 + .../site-packages/pip/_vendor/idna/compat.py | 13 + .../site-packages/pip/_vendor/idna/core.py | 400 + .../pip/_vendor/idna/idnadata.py | 2151 ++ .../pip/_vendor/idna/intranges.py | 54 + .../pip/_vendor/idna/package_data.py | 2 + .../site-packages/pip/_vendor/idna/py.typed | 0 .../pip/_vendor/idna/uts46data.py | 8600 ++++++ .../pip/_vendor/msgpack/__init__.py | 57 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1010 + .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/py.typed | 0 .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 802 + .../pip/_vendor/packaging/tags.py | 487 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pkg_resources/__init__.py | 3361 +++ .../pip/_vendor/platformdirs/__init__.py | 566 + .../pip/_vendor/platformdirs/__main__.py | 53 + .../pip/_vendor/platformdirs/android.py | 210 + .../pip/_vendor/platformdirs/api.py | 223 + .../pip/_vendor/platformdirs/macos.py | 91 + .../pip/_vendor/platformdirs/py.typed | 0 .../pip/_vendor/platformdirs/unix.py | 223 + .../pip/_vendor/platformdirs/version.py | 4 + .../pip/_vendor/platformdirs/windows.py | 255 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../pip/_vendor/pygments/cmdline.py | 668 + .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 71 + .../pip/_vendor/pygments/filters/__init__.py | 940 + .../pip/_vendor/pygments/formatter.py | 124 + .../_vendor/pygments/formatters/__init__.py | 158 + .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 170 + .../pip/_vendor/pygments/formatters/html.py | 989 + .../pip/_vendor/pygments/formatters/img.py | 645 + .../pip/_vendor/pygments/formatters/irc.py | 154 + .../pip/_vendor/pygments/formatters/latex.py | 521 + .../pip/_vendor/pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 146 + .../pip/_vendor/pygments/formatters/svg.py | 188 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 943 + .../pip/_vendor/pygments/lexers/__init__.py | 362 + .../pip/_vendor/pygments/lexers/_mapping.py | 559 + .../pip/_vendor/pygments/lexers/python.py | 1198 + .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 88 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 217 + .../pip/_vendor/pygments/style.py | 197 + .../pip/_vendor/pygments/styles/__init__.py | 103 + .../pip/_vendor/pygments/token.py | 213 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 330 + .../pip/_vendor/pyparsing/__init__.py | 322 + .../pip/_vendor/pyparsing/actions.py | 217 + .../pip/_vendor/pyparsing/common.py | 432 + .../pip/_vendor/pyparsing/core.py | 6115 ++++ .../pip/_vendor/pyparsing/diagram/__init__.py | 656 + .../pip/_vendor/pyparsing/exceptions.py | 299 + .../pip/_vendor/pyparsing/helpers.py | 1100 + .../pip/_vendor/pyparsing/py.typed | 0 .../pip/_vendor/pyparsing/results.py | 796 + .../pip/_vendor/pyparsing/testing.py | 331 + .../pip/_vendor/pyparsing/unicode.py | 361 + .../pip/_vendor/pyparsing/util.py | 284 + .../pip/_vendor/pyproject_hooks/__init__.py | 23 + .../pip/_vendor/pyproject_hooks/_compat.py | 8 + .../pip/_vendor/pyproject_hooks/_impl.py | 330 + .../pyproject_hooks/_in_process/__init__.py | 18 + .../_in_process/_in_process.py | 353 + .../pip/_vendor/requests/__init__.py | 182 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 50 + .../pip/_vendor/requests/adapters.py | 538 + .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 315 + .../pip/_vendor/requests/certs.py | 24 + .../pip/_vendor/requests/compat.py | 67 + .../pip/_vendor/requests/cookies.py | 561 + .../pip/_vendor/requests/exceptions.py | 141 + .../pip/_vendor/requests/help.py | 131 + .../pip/_vendor/requests/hooks.py | 33 + .../pip/_vendor/requests/models.py | 1034 + .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 833 + .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1094 + .../pip/_vendor/resolvelib/__init__.py | 26 + .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/py.typed | 0 .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 547 + .../pip/_vendor/resolvelib/structs.py | 170 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 274 + .../pip/_vendor/rich/_cell_widths.py | 451 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 76 + .../pip/_vendor/rich/_extension.py | 10 + .../site-packages/pip/_vendor/rich/_fileno.py | 24 + .../pip/_vendor/rich/_inspect.py | 270 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 69 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 160 + .../pip/_vendor/rich/_spinners.py | 482 + .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_win32_console.py | 662 + .../pip/_vendor/rich/_windows.py | 72 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 56 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 311 + .../site-packages/pip/_vendor/rich/ansi.py | 240 + .../site-packages/pip/_vendor/rich/bar.py | 94 + .../site-packages/pip/_vendor/rich/box.py | 517 + .../site-packages/pip/_vendor/rich/cells.py | 154 + .../site-packages/pip/_vendor/rich/color.py | 622 + .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2633 ++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 225 + .../pip/_vendor/rich/default_styles.py | 190 + .../pip/_vendor/rich/diagnose.py | 37 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 57 + .../pip/_vendor/rich/filesize.py | 89 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 140 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 443 + .../site-packages/pip/_vendor/rich/live.py | 375 + .../pip/_vendor/rich/live_render.py | 113 + .../site-packages/pip/_vendor/rich/logging.py | 289 + .../site-packages/pip/_vendor/rich/markup.py | 246 + .../site-packages/pip/_vendor/rich/measure.py | 151 + .../site-packages/pip/_vendor/rich/padding.py | 141 + .../site-packages/pip/_vendor/rich/pager.py | 34 + .../site-packages/pip/_vendor/rich/palette.py | 100 + .../site-packages/pip/_vendor/rich/panel.py | 308 + .../site-packages/pip/_vendor/rich/pretty.py | 994 + .../pip/_vendor/rich/progress.py | 1702 ++ .../pip/_vendor/rich/progress_bar.py | 224 + .../site-packages/pip/_vendor/rich/prompt.py | 376 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/py.typed | 0 .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 130 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 739 + .../site-packages/pip/_vendor/rich/spinner.py | 137 + .../site-packages/pip/_vendor/rich/status.py | 132 + .../site-packages/pip/_vendor/rich/style.py | 796 + .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 948 + .../site-packages/pip/_vendor/rich/table.py | 1002 + .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1307 + .../site-packages/pip/_vendor/rich/theme.py | 115 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 756 + .../site-packages/pip/_vendor/rich/tree.py | 251 + .../site-packages/pip/_vendor/six.py | 998 + .../pip/_vendor/tenacity/__init__.py | 608 + .../pip/_vendor/tenacity/_asyncio.py | 94 + .../pip/_vendor/tenacity/_utils.py | 76 + .../pip/_vendor/tenacity/after.py | 51 + .../pip/_vendor/tenacity/before.py | 46 + .../pip/_vendor/tenacity/before_sleep.py | 71 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/py.typed | 0 .../pip/_vendor/tenacity/retry.py | 272 + .../pip/_vendor/tenacity/stop.py | 103 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 228 + .../pip/_vendor/tomli/__init__.py | 11 + .../pip/_vendor/tomli/_parser.py | 691 + .../site-packages/pip/_vendor/tomli/_re.py | 107 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../site-packages/pip/_vendor/tomli/py.typed | 1 + .../pip/_vendor/truststore/__init__.py | 13 + .../pip/_vendor/truststore/_api.py | 302 + .../pip/_vendor/truststore/_macos.py | 501 + .../pip/_vendor/truststore/_openssl.py | 66 + .../pip/_vendor/truststore/_ssl_constants.py | 31 + .../pip/_vendor/truststore/_windows.py | 554 + .../pip/_vendor/truststore/py.typed | 0 .../pip/_vendor/typing_extensions.py | 3072 ++ .../pip/_vendor/urllib3/__init__.py | 102 + .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 572 + .../pip/_vendor/urllib3/connectionpool.py | 1132 + .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 397 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 518 + .../urllib3/contrib/securetransport.py | 921 + .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 51 + .../packages/backports/weakref_finalize.py | 155 + .../pip/_vendor/urllib3/packages/six.py | 1076 + .../pip/_vendor/urllib3/poolmanager.py | 537 + .../pip/_vendor/urllib3/request.py | 191 + .../pip/_vendor/urllib3/response.py | 879 + .../pip/_vendor/urllib3/util/__init__.py | 49 + .../pip/_vendor/urllib3/util/connection.py | 149 + .../pip/_vendor/urllib3/util/proxy.py | 57 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 137 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 620 + .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 271 + .../pip/_vendor/urllib3/util/url.py | 435 + .../pip/_vendor/urllib3/util/wait.py | 152 + .../site-packages/pip/_vendor/vendor.txt | 24 + .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.11/site-packages/pip/py.typed | 4 + .../site-packages/pkg_resources/__init__.py | 3296 ++ .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 + .../_vendor/importlib_resources/__init__.py | 36 + .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 104 + .../_vendor/importlib_resources/_compat.py | 98 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 121 + .../_vendor/importlib_resources/abc.py | 137 + .../_vendor/importlib_resources/readers.py | 122 + .../_vendor/importlib_resources/simple.py | 116 + .../pkg_resources/_vendor/jaraco/__init__.py | 0 .../pkg_resources/_vendor/jaraco/context.py | 213 + .../pkg_resources/_vendor/jaraco/functools.py | 525 + .../_vendor/jaraco/text/__init__.py | 599 + .../_vendor/more_itertools/__init__.py | 4 + .../_vendor/more_itertools/more.py | 4316 +++ .../_vendor/more_itertools/recipes.py | 698 + .../_vendor/packaging/__about__.py | 26 + .../_vendor/packaging/__init__.py | 25 + .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 61 + .../_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 802 + .../pkg_resources/_vendor/packaging/tags.py | 487 + .../pkg_resources/_vendor/packaging/utils.py | 136 + .../_vendor/packaging/version.py | 504 + .../_vendor/pyparsing/__init__.py | 331 + .../_vendor/pyparsing/actions.py | 207 + .../pkg_resources/_vendor/pyparsing/common.py | 424 + .../pkg_resources/_vendor/pyparsing/core.py | 5814 ++++ .../_vendor/pyparsing/diagram/__init__.py | 642 + .../_vendor/pyparsing/exceptions.py | 267 + .../_vendor/pyparsing/helpers.py | 1088 + .../_vendor/pyparsing/results.py | 760 + .../_vendor/pyparsing/testing.py | 331 + .../_vendor/pyparsing/unicode.py | 352 + .../pkg_resources/_vendor/pyparsing/util.py | 235 + .../pkg_resources/_vendor/zipp.py | 329 + .../pkg_resources/extern/__init__.py | 76 + .../pkginfo-1.9.6.dist-info/INSTALLER | 1 + .../pkginfo-1.9.6.dist-info/LICENSE.txt | 21 + .../pkginfo-1.9.6.dist-info/METADATA | 411 + .../pkginfo-1.9.6.dist-info/RECORD | 60 + .../pkginfo-1.9.6.dist-info/REQUESTED | 0 .../pkginfo-1.9.6.dist-info/WHEEL | 5 + .../pkginfo-1.9.6.dist-info/entry_points.txt | 3 + .../pkginfo-1.9.6.dist-info/top_level.txt | 1 + .../site-packages/pkginfo/__init__.py | 9 + .../site-packages/pkginfo/__init__.pyi | 8 + .../python3.11/site-packages/pkginfo/bdist.py | 39 + .../site-packages/pkginfo/bdist.pyi | 7 + .../site-packages/pkginfo/commandline.py | 229 + .../site-packages/pkginfo/commandline.pyi | 35 + .../site-packages/pkginfo/develop.py | 46 + .../site-packages/pkginfo/develop.pyi | 7 + .../site-packages/pkginfo/distribution.py | 162 + .../site-packages/pkginfo/distribution.pyi | 57 + .../python3.11/site-packages/pkginfo/index.py | 15 + .../site-packages/pkginfo/index.pyi | 5 + .../site-packages/pkginfo/installed.py | 62 + .../site-packages/pkginfo/installed.pyi | 10 + .../python3.11/site-packages/pkginfo/py.typed | 0 .../python3.11/site-packages/pkginfo/sdist.py | 75 + .../site-packages/pkginfo/sdist.pyi | 11 + .../site-packages/pkginfo/tests/__init__.py | 37 + .../site-packages/pkginfo/tests/test_bdist.py | 60 + .../pkginfo/tests/test_commandline.py | 345 + .../pkginfo/tests/test_develop.py | 27 + .../pkginfo/tests/test_distribution.py | 477 + .../site-packages/pkginfo/tests/test_index.py | 76 + .../pkginfo/tests/test_installed.py | 139 + .../site-packages/pkginfo/tests/test_sdist.py | 156 + .../site-packages/pkginfo/tests/test_utils.py | 176 + .../site-packages/pkginfo/tests/test_wheel.py | 117 + .../python3.11/site-packages/pkginfo/utils.py | 62 + .../site-packages/pkginfo/utils.pyi | 5 + .../python3.11/site-packages/pkginfo/wheel.py | 55 + .../site-packages/pkginfo/wheel.pyi | 7 + .../platformdirs-3.0.0.dist-info/INSTALLER | 1 + .../platformdirs-3.0.0.dist-info/METADATA | 254 + .../platformdirs-3.0.0.dist-info/RECORD | 23 + .../platformdirs-3.0.0.dist-info/REQUESTED | 0 .../platformdirs-3.0.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../site-packages/platformdirs/__init__.py | 341 + .../site-packages/platformdirs/__main__.py | 46 + .../site-packages/platformdirs/android.py | 120 + .../site-packages/platformdirs/api.py | 156 + .../site-packages/platformdirs/macos.py | 64 + .../site-packages/platformdirs/py.typed | 0 .../site-packages/platformdirs/unix.py | 181 + .../site-packages/platformdirs/version.py | 4 + .../site-packages/platformdirs/windows.py | 184 + .../pluggy-1.6.0.dist-info/INSTALLER | 1 + .../pluggy-1.6.0.dist-info/METADATA | 152 + .../pluggy-1.6.0.dist-info/RECORD | 24 + .../pluggy-1.6.0.dist-info/REQUESTED | 0 .../pluggy-1.6.0.dist-info/WHEEL | 5 + .../pluggy-1.6.0.dist-info/licenses/LICENSE | 21 + .../pluggy-1.6.0.dist-info/top_level.txt | 1 + .../site-packages/pluggy/__init__.py | 30 + .../site-packages/pluggy/_callers.py | 169 + .../python3.11/site-packages/pluggy/_hooks.py | 714 + .../site-packages/pluggy/_manager.py | 523 + .../site-packages/pluggy/_result.py | 107 + .../site-packages/pluggy/_tracing.py | 72 + .../site-packages/pluggy/_version.py | 21 + .../site-packages/pluggy/_warnings.py | 27 + .../python3.11/site-packages/pluggy/py.typed | 0 .venv/lib/python3.11/site-packages/py.py | 15 + .../pycodestyle-2.13.0.dist-info/INSTALLER | 1 + .../pycodestyle-2.13.0.dist-info/LICENSE | 25 + .../pycodestyle-2.13.0.dist-info/METADATA | 131 + .../pycodestyle-2.13.0.dist-info/RECORD | 11 + .../pycodestyle-2.13.0.dist-info/REQUESTED | 0 .../pycodestyle-2.13.0.dist-info/WHEEL | 6 + .../entry_points.txt | 2 + .../top_level.txt | 1 + .../python3.11/site-packages/pycodestyle.py | 2673 ++ .../pycparser-2.21.dist-info/INSTALLER | 1 + .../pycparser-2.21.dist-info/LICENSE | 27 + .../pycparser-2.21.dist-info/METADATA | 31 + .../pycparser-2.21.dist-info/RECORD | 42 + .../pycparser-2.21.dist-info/REQUESTED | 0 .../pycparser-2.21.dist-info/WHEEL | 6 + .../pycparser-2.21.dist-info/top_level.txt | 1 + .../site-packages/pycparser/__init__.py | 90 + .../site-packages/pycparser/_ast_gen.py | 336 + .../site-packages/pycparser/_build_tables.py | 37 + .../site-packages/pycparser/_c_ast.cfg | 195 + .../site-packages/pycparser/ast_transforms.py | 164 + .../site-packages/pycparser/c_ast.py | 1125 + .../site-packages/pycparser/c_generator.py | 502 + .../site-packages/pycparser/c_lexer.py | 554 + .../site-packages/pycparser/c_parser.py | 1936 ++ .../site-packages/pycparser/lextab.py | 10 + .../site-packages/pycparser/ply/__init__.py | 5 + .../site-packages/pycparser/ply/cpp.py | 905 + .../site-packages/pycparser/ply/ctokens.py | 133 + .../site-packages/pycparser/ply/lex.py | 1099 + .../site-packages/pycparser/ply/yacc.py | 3494 +++ .../site-packages/pycparser/ply/ygen.py | 74 + .../site-packages/pycparser/plyparser.py | 133 + .../site-packages/pycparser/yacctab.py | 366 + .../site-packages/pygments/__init__.py | 82 + .../site-packages/pygments/__main__.py | 17 + .../site-packages/pygments/cmdline.py | 668 + .../site-packages/pygments/console.py | 70 + .../site-packages/pygments/filter.py | 71 + .../pygments/filters/__init__.py | 940 + .../site-packages/pygments/formatter.py | 94 + .../pygments/formatters/__init__.py | 142 + .../pygments/formatters/_mapping.py | 23 + .../pygments/formatters/bbcode.py | 108 + .../pygments/formatters/groff.py | 170 + .../site-packages/pygments/formatters/html.py | 991 + .../site-packages/pygments/formatters/img.py | 645 + .../site-packages/pygments/formatters/irc.py | 154 + .../pygments/formatters/latex.py | 521 + .../pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../site-packages/pygments/formatters/rtf.py | 146 + .../site-packages/pygments/formatters/svg.py | 188 + .../pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../site-packages/pygments/lexer.py | 883 + .../site-packages/pygments/lexers/__init__.py | 334 + .../pygments/lexers/_ada_builtins.py | 103 + .../pygments/lexers/_asy_builtins.py | 1644 + .../pygments/lexers/_cl_builtins.py | 231 + .../pygments/lexers/_cocoa_builtins.py | 75 + .../pygments/lexers/_csound_builtins.py | 1780 ++ .../pygments/lexers/_css_builtins.py | 558 + .../pygments/lexers/_julia_builtins.py | 411 + .../pygments/lexers/_lasso_builtins.py | 5326 ++++ .../pygments/lexers/_lilypond_builtins.py | 4886 +++ .../pygments/lexers/_lua_builtins.py | 285 + .../site-packages/pygments/lexers/_mapping.py | 553 + .../pygments/lexers/_mql_builtins.py | 1171 + .../pygments/lexers/_mysql_builtins.py | 1335 + .../pygments/lexers/_openedge_builtins.py | 2600 ++ .../pygments/lexers/_php_builtins.py | 3325 +++ .../pygments/lexers/_postgres_builtins.py | 684 + .../pygments/lexers/_qlik_builtins.py | 666 + .../pygments/lexers/_scheme_builtins.py | 1609 + .../pygments/lexers/_scilab_builtins.py | 3093 ++ .../pygments/lexers/_sourcemod_builtins.py | 1151 + .../pygments/lexers/_stan_builtins.py | 648 + .../pygments/lexers/_stata_builtins.py | 457 + .../pygments/lexers/_tsql_builtins.py | 1003 + .../pygments/lexers/_usd_builtins.py | 112 + .../pygments/lexers/_vbscript_builtins.py | 279 + .../pygments/lexers/_vim_builtins.py | 1938 ++ .../pygments/lexers/actionscript.py | 245 + .../site-packages/pygments/lexers/ada.py | 144 + .../site-packages/pygments/lexers/agile.py | 23 + .../site-packages/pygments/lexers/algebra.py | 302 + .../site-packages/pygments/lexers/ambient.py | 76 + .../site-packages/pygments/lexers/amdgpu.py | 53 + .../site-packages/pygments/lexers/ampl.py | 88 + .../site-packages/pygments/lexers/apdlexer.py | 447 + .../site-packages/pygments/lexers/apl.py | 104 + .../pygments/lexers/archetype.py | 319 + .../site-packages/pygments/lexers/arrow.py | 117 + .../site-packages/pygments/lexers/arturo.py | 250 + .../site-packages/pygments/lexers/asc.py | 55 + .../site-packages/pygments/lexers/asm.py | 1037 + .../pygments/lexers/automation.py | 381 + .../site-packages/pygments/lexers/bare.py | 102 + .../site-packages/pygments/lexers/basic.py | 665 + .../site-packages/pygments/lexers/bdd.py | 58 + .../site-packages/pygments/lexers/berry.py | 99 + .../site-packages/pygments/lexers/bibtex.py | 159 + .../site-packages/pygments/lexers/boa.py | 97 + .../site-packages/pygments/lexers/business.py | 626 + .../site-packages/pygments/lexers/c_cpp.py | 409 + .../site-packages/pygments/lexers/c_like.py | 666 + .../pygments/lexers/capnproto.py | 75 + .../site-packages/pygments/lexers/cddl.py | 173 + .../site-packages/pygments/lexers/chapel.py | 136 + .../site-packages/pygments/lexers/clean.py | 179 + .../site-packages/pygments/lexers/comal.py | 80 + .../site-packages/pygments/lexers/compiled.py | 34 + .../site-packages/pygments/lexers/configs.py | 1174 + .../site-packages/pygments/lexers/console.py | 114 + .../site-packages/pygments/lexers/cplint.py | 44 + .../site-packages/pygments/lexers/crystal.py | 365 + .../site-packages/pygments/lexers/csound.py | 468 + .../site-packages/pygments/lexers/css.py | 602 + .../site-packages/pygments/lexers/d.py | 258 + .../site-packages/pygments/lexers/dalvik.py | 127 + .../site-packages/pygments/lexers/data.py | 767 + .../pygments/lexers/devicetree.py | 109 + .../site-packages/pygments/lexers/diff.py | 165 + .../site-packages/pygments/lexers/dotnet.py | 729 + .../site-packages/pygments/lexers/dsls.py | 981 + .../site-packages/pygments/lexers/dylan.py | 287 + .../site-packages/pygments/lexers/ecl.py | 145 + .../site-packages/pygments/lexers/eiffel.py | 69 + .../site-packages/pygments/lexers/elm.py | 124 + .../site-packages/pygments/lexers/elpi.py | 165 + .../site-packages/pygments/lexers/email.py | 132 + .../site-packages/pygments/lexers/erlang.py | 528 + .../site-packages/pygments/lexers/esoteric.py | 301 + .../site-packages/pygments/lexers/ezhil.py | 77 + .../site-packages/pygments/lexers/factor.py | 364 + .../site-packages/pygments/lexers/fantom.py | 251 + .../site-packages/pygments/lexers/felix.py | 276 + .../site-packages/pygments/lexers/fift.py | 67 + .../pygments/lexers/floscript.py | 82 + .../site-packages/pygments/lexers/forth.py | 179 + .../site-packages/pygments/lexers/fortran.py | 213 + .../site-packages/pygments/lexers/foxpro.py | 427 + .../site-packages/pygments/lexers/freefem.py | 894 + .../site-packages/pygments/lexers/func.py | 108 + .../pygments/lexers/functional.py | 20 + .../site-packages/pygments/lexers/futhark.py | 106 + .../pygments/lexers/gcodelexer.py | 35 + .../site-packages/pygments/lexers/gdscript.py | 188 + .../site-packages/pygments/lexers/go.py | 98 + .../pygments/lexers/grammar_notation.py | 265 + .../site-packages/pygments/lexers/graph.py | 105 + .../site-packages/pygments/lexers/graphics.py | 797 + .../site-packages/pygments/lexers/graphviz.py | 59 + .../site-packages/pygments/lexers/gsql.py | 104 + .../site-packages/pygments/lexers/haskell.py | 871 + .../site-packages/pygments/lexers/haxe.py | 937 + .../site-packages/pygments/lexers/hdl.py | 465 + .../site-packages/pygments/lexers/hexdump.py | 102 + .../site-packages/pygments/lexers/html.py | 605 + .../site-packages/pygments/lexers/idl.py | 285 + .../site-packages/pygments/lexers/igor.py | 420 + .../site-packages/pygments/lexers/inferno.py | 96 + .../pygments/lexers/installers.py | 327 + .../pygments/lexers/int_fiction.py | 1382 + .../site-packages/pygments/lexers/iolang.py | 62 + .../site-packages/pygments/lexers/j.py | 152 + .../pygments/lexers/javascript.py | 1588 + .../site-packages/pygments/lexers/jmespath.py | 68 + .../site-packages/pygments/lexers/jslt.py | 95 + .../site-packages/pygments/lexers/jsonnet.py | 168 + .../site-packages/pygments/lexers/julia.py | 294 + .../site-packages/pygments/lexers/jvm.py | 1820 ++ .../site-packages/pygments/lexers/kuin.py | 333 + .../site-packages/pygments/lexers/lilypond.py | 226 + .../site-packages/pygments/lexers/lisp.py | 2838 ++ .../pygments/lexers/macaulay2.py | 1739 ++ .../site-packages/pygments/lexers/make.py | 209 + .../site-packages/pygments/lexers/markup.py | 765 + .../site-packages/pygments/lexers/math.py | 20 + .../site-packages/pygments/lexers/matlab.py | 3308 ++ .../site-packages/pygments/lexers/maxima.py | 85 + .../site-packages/pygments/lexers/meson.py | 140 + .../site-packages/pygments/lexers/mime.py | 210 + .../pygments/lexers/minecraft.py | 394 + .../site-packages/pygments/lexers/mips.py | 128 + .../site-packages/pygments/lexers/ml.py | 960 + .../site-packages/pygments/lexers/modeling.py | 369 + .../site-packages/pygments/lexers/modula2.py | 1580 + .../site-packages/pygments/lexers/monte.py | 204 + .../site-packages/pygments/lexers/mosel.py | 447 + .../site-packages/pygments/lexers/ncl.py | 893 + .../site-packages/pygments/lexers/nimrod.py | 200 + .../site-packages/pygments/lexers/nit.py | 64 + .../site-packages/pygments/lexers/nix.py | 135 + .../site-packages/pygments/lexers/oberon.py | 120 + .../pygments/lexers/objective.py | 505 + .../site-packages/pygments/lexers/ooc.py | 85 + .../site-packages/pygments/lexers/other.py | 40 + .../site-packages/pygments/lexers/parasail.py | 79 + .../site-packages/pygments/lexers/parsers.py | 801 + .../site-packages/pygments/lexers/pascal.py | 641 + .../site-packages/pygments/lexers/pawn.py | 202 + .../site-packages/pygments/lexers/perl.py | 733 + .../site-packages/pygments/lexers/phix.py | 364 + .../site-packages/pygments/lexers/php.py | 319 + .../pygments/lexers/pointless.py | 71 + .../site-packages/pygments/lexers/pony.py | 93 + .../site-packages/pygments/lexers/praat.py | 304 + .../site-packages/pygments/lexers/procfile.py | 42 + .../site-packages/pygments/lexers/prolog.py | 304 + .../site-packages/pygments/lexers/promql.py | 175 + .../site-packages/pygments/lexers/python.py | 1204 + .../site-packages/pygments/lexers/q.py | 188 + .../site-packages/pygments/lexers/qlik.py | 117 + .../site-packages/pygments/lexers/qvt.py | 151 + .../site-packages/pygments/lexers/r.py | 190 + .../site-packages/pygments/lexers/rdf.py | 462 + .../site-packages/pygments/lexers/rebol.py | 430 + .../site-packages/pygments/lexers/resource.py | 84 + .../site-packages/pygments/lexers/ride.py | 139 + .../site-packages/pygments/lexers/rita.py | 43 + .../site-packages/pygments/lexers/rnc.py | 67 + .../site-packages/pygments/lexers/roboconf.py | 81 + .../pygments/lexers/robotframework.py | 552 + .../site-packages/pygments/lexers/ruby.py | 523 + .../site-packages/pygments/lexers/rust.py | 223 + .../site-packages/pygments/lexers/sas.py | 227 + .../site-packages/pygments/lexers/savi.py | 170 + .../site-packages/pygments/lexers/scdoc.py | 79 + .../pygments/lexers/scripting.py | 1286 + .../site-packages/pygments/lexers/sgf.py | 60 + .../site-packages/pygments/lexers/shell.py | 918 + .../site-packages/pygments/lexers/sieve.py | 78 + .../site-packages/pygments/lexers/slash.py | 184 + .../pygments/lexers/smalltalk.py | 196 + .../site-packages/pygments/lexers/smithy.py | 78 + .../site-packages/pygments/lexers/smv.py | 78 + .../site-packages/pygments/lexers/snobol.py | 82 + .../site-packages/pygments/lexers/solidity.py | 87 + .../site-packages/pygments/lexers/sophia.py | 103 + .../site-packages/pygments/lexers/special.py | 116 + .../site-packages/pygments/lexers/spice.py | 71 + .../site-packages/pygments/lexers/sql.py | 838 + .../site-packages/pygments/lexers/srcinfo.py | 62 + .../site-packages/pygments/lexers/stata.py | 171 + .../pygments/lexers/supercollider.py | 95 + .../site-packages/pygments/lexers/tal.py | 74 + .../site-packages/pygments/lexers/tcl.py | 149 + .../site-packages/pygments/lexers/teal.py | 89 + .../pygments/lexers/templates.py | 2300 ++ .../site-packages/pygments/lexers/teraterm.py | 326 + .../site-packages/pygments/lexers/testing.py | 210 + .../site-packages/pygments/lexers/text.py | 26 + .../site-packages/pygments/lexers/textedit.py | 202 + .../site-packages/pygments/lexers/textfmts.py | 431 + .../site-packages/pygments/lexers/theorem.py | 484 + .../site-packages/pygments/lexers/thingsdb.py | 116 + .../site-packages/pygments/lexers/tlb.py | 57 + .../site-packages/pygments/lexers/tnt.py | 271 + .../pygments/lexers/trafficscript.py | 51 + .../pygments/lexers/typoscript.py | 217 + .../site-packages/pygments/lexers/ul4.py | 267 + .../site-packages/pygments/lexers/unicon.py | 411 + .../site-packages/pygments/lexers/urbi.py | 145 + .../site-packages/pygments/lexers/usd.py | 90 + .../site-packages/pygments/lexers/varnish.py | 189 + .../pygments/lexers/verification.py | 114 + .../site-packages/pygments/lexers/web.py | 23 + .../pygments/lexers/webassembly.py | 120 + .../site-packages/pygments/lexers/webidl.py | 299 + .../site-packages/pygments/lexers/webmisc.py | 1010 + .../site-packages/pygments/lexers/whiley.py | 116 + .../site-packages/pygments/lexers/wowtoc.py | 120 + .../site-packages/pygments/lexers/wren.py | 99 + .../site-packages/pygments/lexers/x10.py | 67 + .../site-packages/pygments/lexers/xorg.py | 37 + .../site-packages/pygments/lexers/yang.py | 104 + .../site-packages/pygments/lexers/zig.py | 124 + .../site-packages/pygments/modeline.py | 43 + .../site-packages/pygments/plugin.py | 88 + .../site-packages/pygments/regexopt.py | 91 + .../site-packages/pygments/scanner.py | 104 + .../site-packages/pygments/sphinxext.py | 217 + .../site-packages/pygments/style.py | 197 + .../site-packages/pygments/styles/__init__.py | 97 + .../site-packages/pygments/styles/abap.py | 28 + .../site-packages/pygments/styles/algol.py | 61 + .../site-packages/pygments/styles/algol_nu.py | 61 + .../site-packages/pygments/styles/arduino.py | 96 + .../site-packages/pygments/styles/autumn.py | 62 + .../site-packages/pygments/styles/borland.py | 48 + .../site-packages/pygments/styles/bw.py | 47 + .../site-packages/pygments/styles/colorful.py | 78 + .../site-packages/pygments/styles/default.py | 71 + .../site-packages/pygments/styles/dracula.py | 102 + .../site-packages/pygments/styles/emacs.py | 70 + .../site-packages/pygments/styles/friendly.py | 71 + .../pygments/styles/friendly_grayscale.py | 75 + .../site-packages/pygments/styles/fruity.py | 41 + .../site-packages/pygments/styles/gh_dark.py | 107 + .../site-packages/pygments/styles/gruvbox.py | 109 + .../site-packages/pygments/styles/igor.py | 27 + .../site-packages/pygments/styles/inkpot.py | 67 + .../site-packages/pygments/styles/lilypond.py | 56 + .../site-packages/pygments/styles/lovelace.py | 94 + .../site-packages/pygments/styles/manni.py | 74 + .../site-packages/pygments/styles/material.py | 117 + .../site-packages/pygments/styles/monokai.py | 106 + .../site-packages/pygments/styles/murphy.py | 77 + .../site-packages/pygments/styles/native.py | 65 + .../site-packages/pygments/styles/nord.py | 150 + .../site-packages/pygments/styles/onedark.py | 59 + .../pygments/styles/paraiso_dark.py | 119 + .../pygments/styles/paraiso_light.py | 119 + .../site-packages/pygments/styles/pastie.py | 72 + .../site-packages/pygments/styles/perldoc.py | 67 + .../pygments/styles/rainbow_dash.py | 88 + .../site-packages/pygments/styles/rrt.py | 33 + .../site-packages/pygments/styles/sas.py | 41 + .../pygments/styles/solarized.py | 136 + .../pygments/styles/staroffice.py | 26 + .../pygments/styles/stata_dark.py | 38 + .../pygments/styles/stata_light.py | 37 + .../site-packages/pygments/styles/tango.py | 139 + .../site-packages/pygments/styles/trac.py | 60 + .../site-packages/pygments/styles/vim.py | 61 + .../site-packages/pygments/styles/vs.py | 36 + .../site-packages/pygments/styles/xcode.py | 48 + .../site-packages/pygments/styles/zenburn.py | 78 + .../site-packages/pygments/token.py | 213 + .../site-packages/pygments/unistring.py | 153 + .../python3.11/site-packages/pygments/util.py | 308 + .../pytest-8.4.2.dist-info/INSTALLER | 1 + .../pytest-8.4.2.dist-info/METADATA | 215 + .../pytest-8.4.2.dist-info/RECORD | 157 + .../pytest-8.4.2.dist-info/REQUESTED | 0 .../pytest-8.4.2.dist-info/WHEEL | 5 + .../pytest-8.4.2.dist-info/entry_points.txt | 3 + .../pytest-8.4.2.dist-info/licenses/AUTHORS | 497 + .../pytest-8.4.2.dist-info/licenses/LICENSE | 21 + .../pytest-8.4.2.dist-info/top_level.txt | 3 + .../site-packages/pytest/__init__.py | 180 + .../site-packages/pytest/__main__.py | 9 + .../python3.11/site-packages/pytest/py.typed | 0 .../pytest_xdist-3.2.0.dist-info/INSTALLER | 1 + .../pytest_xdist-3.2.0.dist-info/LICENSE | 18 + .../pytest_xdist-3.2.0.dist-info/METADATA | 77 + .../pytest_xdist-3.2.0.dist-info/RECORD | 42 + .../pytest_xdist-3.2.0.dist-info/REQUESTED | 0 .../pytest_xdist-3.2.0.dist-info/WHEEL | 5 + .../entry_points.txt | 3 + .../top_level.txt | 1 + .../pyyaml-6.0.3.dist-info/INSTALLER | 1 + .../pyyaml-6.0.3.dist-info/METADATA | 59 + .../pyyaml-6.0.3.dist-info/RECORD | 43 + .../pyyaml-6.0.3.dist-info/WHEEL | 7 + .../pyyaml-6.0.3.dist-info/licenses/LICENSE | 20 + .../pyyaml-6.0.3.dist-info/top_level.txt | 2 + .../readme_renderer-37.3.dist-info/INSTALLER | 1 + .../readme_renderer-37.3.dist-info/LICENSE | 174 + .../readme_renderer-37.3.dist-info/METADATA | 68 + .../readme_renderer-37.3.dist-info/RECORD | 22 + .../readme_renderer-37.3.dist-info/REQUESTED | 0 .../readme_renderer-37.3.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../readme_renderer/__about__.py | 38 + .../site-packages/readme_renderer/__init__.py | 13 + .../site-packages/readme_renderer/__main__.py | 19 + .../site-packages/readme_renderer/clean.py | 132 + .../site-packages/readme_renderer/markdown.py | 123 + .../site-packages/readme_renderer/py.typed | 0 .../site-packages/readme_renderer/rst.py | 135 + .../site-packages/readme_renderer/txt.py | 24 + .../requests-2.32.5.dist-info/INSTALLER | 1 + .../requests-2.32.5.dist-info/METADATA | 133 + .../requests-2.32.5.dist-info/RECORD | 42 + .../requests-2.32.5.dist-info/WHEEL | 5 + .../licenses/LICENSE | 175 + .../requests-2.32.5.dist-info/top_level.txt | 1 + .../site-packages/requests/__init__.py | 184 + .../site-packages/requests/__version__.py | 14 + .../site-packages/requests/_internal_utils.py | 50 + .../site-packages/requests/adapters.py | 696 + .../python3.11/site-packages/requests/api.py | 157 + .../python3.11/site-packages/requests/auth.py | 314 + .../site-packages/requests/certs.py | 17 + .../site-packages/requests/compat.py | 106 + .../site-packages/requests/cookies.py | 561 + .../site-packages/requests/exceptions.py | 151 + .../python3.11/site-packages/requests/help.py | 134 + .../site-packages/requests/hooks.py | 33 + .../site-packages/requests/models.py | 1039 + .../site-packages/requests/packages.py | 23 + .../site-packages/requests/sessions.py | 831 + .../site-packages/requests/status_codes.py | 128 + .../site-packages/requests/structures.py | 99 + .../site-packages/requests/utils.py | 1086 + .../AUTHORS.rst | 57 + .../INSTALLER | 1 + .../LICENSE | 13 + .../METADATA | 497 + .../requests_toolbelt-0.10.1.dist-info/RECORD | 76 + .../REQUESTED | 0 .../requests_toolbelt-0.10.1.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../requests_toolbelt/__init__.py | 34 + .../requests_toolbelt/_compat.py | 311 + .../requests_toolbelt/adapters/__init__.py | 15 + .../requests_toolbelt/adapters/appengine.py | 206 + .../requests_toolbelt/adapters/fingerprint.py | 48 + .../adapters/host_header_ssl.py | 43 + .../adapters/socket_options.py | 129 + .../requests_toolbelt/adapters/source.py | 67 + .../requests_toolbelt/adapters/ssl.py | 66 + .../requests_toolbelt/adapters/x509.py | 196 + .../requests_toolbelt/auth/__init__.py | 0 .../auth/_digest_auth_compat.py | 29 + .../requests_toolbelt/auth/guess.py | 146 + .../requests_toolbelt/auth/handler.py | 142 + .../auth/http_proxy_digest.py | 103 + .../requests_toolbelt/cookies/__init__.py | 0 .../requests_toolbelt/cookies/forgetful.py | 7 + .../downloadutils/__init__.py | 0 .../requests_toolbelt/downloadutils/stream.py | 176 + .../requests_toolbelt/downloadutils/tee.py | 123 + .../requests_toolbelt/exceptions.py | 37 + .../requests_toolbelt/multipart/__init__.py | 31 + .../requests_toolbelt/multipart/decoder.py | 156 + .../requests_toolbelt/multipart/encoder.py | 655 + .../requests_toolbelt/sessions.py | 89 + .../requests_toolbelt/streaming_iterator.py | 116 + .../requests_toolbelt/threaded/__init__.py | 97 + .../requests_toolbelt/threaded/pool.py | 211 + .../requests_toolbelt/threaded/thread.py | 53 + .../requests_toolbelt/utils/__init__.py | 0 .../requests_toolbelt/utils/deprecated.py | 91 + .../requests_toolbelt/utils/dump.py | 198 + .../requests_toolbelt/utils/formdata.py | 108 + .../requests_toolbelt/utils/user_agent.py | 143 + .../rfc3986-2.0.0.dist-info/INSTALLER | 1 + .../rfc3986-2.0.0.dist-info/LICENSE | 13 + .../rfc3986-2.0.0.dist-info/METADATA | 234 + .../rfc3986-2.0.0.dist-info/RECORD | 33 + .../rfc3986-2.0.0.dist-info/REQUESTED | 0 .../rfc3986-2.0.0.dist-info/WHEEL | 6 + .../rfc3986-2.0.0.dist-info/top_level.txt | 1 + .../site-packages/rfc3986/__init__.py | 53 + .../site-packages/rfc3986/_mixin.py | 373 + .../site-packages/rfc3986/abnf_regexp.py | 275 + .../python3.11/site-packages/rfc3986/api.py | 104 + .../site-packages/rfc3986/builder.py | 388 + .../site-packages/rfc3986/compat.py | 59 + .../site-packages/rfc3986/exceptions.py | 120 + .../python3.11/site-packages/rfc3986/iri.py | 161 + .../python3.11/site-packages/rfc3986/misc.py | 131 + .../site-packages/rfc3986/normalizers.py | 171 + .../site-packages/rfc3986/parseresult.py | 474 + .../python3.11/site-packages/rfc3986/uri.py | 160 + .../site-packages/rfc3986/validators.py | 440 + .../rich-13.3.1.dist-info/INSTALLER | 1 + .../rich-13.3.1.dist-info/LICENSE | 19 + .../rich-13.3.1.dist-info/METADATA | 484 + .../rich-13.3.1.dist-info/RECORD | 163 + .../rich-13.3.1.dist-info/REQUESTED | 0 .../site-packages/rich-13.3.1.dist-info/WHEEL | 4 + .../python3.11/site-packages/rich/__init__.py | 177 + .../python3.11/site-packages/rich/__main__.py | 274 + .../site-packages/rich/_cell_widths.py | 451 + .../site-packages/rich/_emoji_codes.py | 3610 +++ .../site-packages/rich/_emoji_replace.py | 32 + .../site-packages/rich/_export_format.py | 78 + .../site-packages/rich/_extension.py | 10 + .../python3.11/site-packages/rich/_fileno.py | 24 + .../python3.11/site-packages/rich/_inspect.py | 270 + .../site-packages/rich/_log_render.py | 94 + .../python3.11/site-packages/rich/_loop.py | 43 + .../site-packages/rich/_null_file.py | 69 + .../site-packages/rich/_palettes.py | 309 + .../python3.11/site-packages/rich/_pick.py | 17 + .../python3.11/site-packages/rich/_ratio.py | 160 + .../site-packages/rich/_spinners.py | 482 + .../python3.11/site-packages/rich/_stack.py | 16 + .../python3.11/site-packages/rich/_timer.py | 19 + .../site-packages/rich/_win32_console.py | 662 + .../python3.11/site-packages/rich/_windows.py | 72 + .../site-packages/rich/_windows_renderer.py | 56 + .../python3.11/site-packages/rich/_wrap.py | 56 + .../lib/python3.11/site-packages/rich/abc.py | 33 + .../python3.11/site-packages/rich/align.py | 311 + .../lib/python3.11/site-packages/rich/ansi.py | 237 + .../lib/python3.11/site-packages/rich/bar.py | 94 + .../lib/python3.11/site-packages/rich/box.py | 517 + .../python3.11/site-packages/rich/cells.py | 154 + .../python3.11/site-packages/rich/color.py | 622 + .../site-packages/rich/color_triplet.py | 38 + .../python3.11/site-packages/rich/columns.py | 187 + .../python3.11/site-packages/rich/console.py | 2629 ++ .../site-packages/rich/constrain.py | 37 + .../site-packages/rich/containers.py | 167 + .../python3.11/site-packages/rich/control.py | 225 + .../site-packages/rich/default_styles.py | 190 + .../python3.11/site-packages/rich/diagnose.py | 37 + .../python3.11/site-packages/rich/emoji.py | 96 + .../python3.11/site-packages/rich/errors.py | 34 + .../site-packages/rich/file_proxy.py | 57 + .../python3.11/site-packages/rich/filesize.py | 89 + .../site-packages/rich/highlighter.py | 232 + .../lib/python3.11/site-packages/rich/json.py | 140 + .../python3.11/site-packages/rich/jupyter.py | 101 + .../python3.11/site-packages/rich/layout.py | 443 + .../lib/python3.11/site-packages/rich/live.py | 373 + .../site-packages/rich/live_render.py | 113 + .../python3.11/site-packages/rich/logging.py | 289 + .../python3.11/site-packages/rich/markdown.py | 677 + .../python3.11/site-packages/rich/markup.py | 246 + .../python3.11/site-packages/rich/measure.py | 151 + .../python3.11/site-packages/rich/padding.py | 141 + .../python3.11/site-packages/rich/pager.py | 34 + .../python3.11/site-packages/rich/palette.py | 100 + .../python3.11/site-packages/rich/panel.py | 308 + .../python3.11/site-packages/rich/pretty.py | 1029 + .../python3.11/site-packages/rich/progress.py | 1702 ++ .../site-packages/rich/progress_bar.py | 224 + .../python3.11/site-packages/rich/prompt.py | 376 + .../python3.11/site-packages/rich/protocol.py | 42 + .../python3.11/site-packages/rich/py.typed | 0 .../python3.11/site-packages/rich/region.py | 10 + .../lib/python3.11/site-packages/rich/repr.py | 149 + .../lib/python3.11/site-packages/rich/rule.py | 130 + .../python3.11/site-packages/rich/scope.py | 86 + .../python3.11/site-packages/rich/screen.py | 54 + .../python3.11/site-packages/rich/segment.py | 739 + .../python3.11/site-packages/rich/spinner.py | 137 + .../python3.11/site-packages/rich/status.py | 132 + .../python3.11/site-packages/rich/style.py | 773 + .../python3.11/site-packages/rich/styled.py | 42 + .../python3.11/site-packages/rich/syntax.py | 945 + .../python3.11/site-packages/rich/table.py | 1002 + .../site-packages/rich/terminal_theme.py | 153 + .../lib/python3.11/site-packages/rich/text.py | 1311 + .../python3.11/site-packages/rich/theme.py | 115 + .../python3.11/site-packages/rich/themes.py | 5 + .../site-packages/rich/traceback.py | 745 + .../lib/python3.11/site-packages/rich/tree.py | 251 + .../saneyaml-0.6.1.dist-info/AUTHORS.rst | 3 + .../saneyaml-0.6.1.dist-info/CHANGELOG.rst | 44 + .../CODE_OF_CONDUCT.rst | 86 + .../saneyaml-0.6.1.dist-info/INSTALLER | 1 + .../saneyaml-0.6.1.dist-info/METADATA | 80 + .../saneyaml-0.6.1.dist-info/NOTICE | 19 + .../saneyaml-0.6.1.dist-info/RECORD | 12 + .../saneyaml-0.6.1.dist-info/WHEEL | 5 + .../apache-2.0.LICENSE | 201 + .../saneyaml-0.6.1.dist-info/top_level.txt | 1 + .../lib/python3.11/site-packages/saneyaml.py | 341 + .../site-packages/secretstorage/__init__.py | 93 + .../site-packages/secretstorage/collection.py | 224 + .../site-packages/secretstorage/defines.py | 20 + .../site-packages/secretstorage/dhcrypto.py | 59 + .../site-packages/secretstorage/exceptions.py | 50 + .../site-packages/secretstorage/item.py | 145 + .../site-packages/secretstorage/py.typed | 0 .../site-packages/secretstorage/util.py | 179 + .../setuptools-65.5.0.dist-info/INSTALLER | 1 + .../setuptools-65.5.0.dist-info/LICENSE | 19 + .../setuptools-65.5.0.dist-info/METADATA | 144 + .../setuptools-65.5.0.dist-info/RECORD | 466 + .../setuptools-65.5.0.dist-info/REQUESTED | 0 .../setuptools-65.5.0.dist-info/WHEEL | 5 + .../entry_points.txt | 57 + .../setuptools-65.5.0.dist-info/top_level.txt | 3 + .../site-packages/setuptools/__init__.py | 247 + .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 24 + .../setuptools/_distutils/_collections.py | 56 + .../setuptools/_distutils/_functools.py | 20 + .../setuptools/_distutils/_macos_compat.py | 12 + .../setuptools/_distutils/_msvccompiler.py | 572 + .../setuptools/_distutils/archive_util.py | 280 + .../setuptools/_distutils/bcppcompiler.py | 408 + .../setuptools/_distutils/ccompiler.py | 1220 + .../setuptools/_distutils/cmd.py | 436 + .../setuptools/_distutils/command/__init__.py | 25 + .../_distutils/command/_framework_compat.py | 55 + .../setuptools/_distutils/command/bdist.py | 157 + .../_distutils/command/bdist_dumb.py | 144 + .../_distutils/command/bdist_rpm.py | 615 + .../setuptools/_distutils/command/build.py | 153 + .../_distutils/command/build_clib.py | 208 + .../_distutils/command/build_ext.py | 787 + .../setuptools/_distutils/command/build_py.py | 407 + .../_distutils/command/build_scripts.py | 173 + .../setuptools/_distutils/command/check.py | 151 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 377 + .../setuptools/_distutils/command/install.py | 814 + .../_distutils/command/install_data.py | 84 + .../_distutils/command/install_egg_info.py | 91 + .../_distutils/command/install_headers.py | 45 + .../_distutils/command/install_lib.py | 238 + .../_distutils/command/install_scripts.py | 61 + .../_distutils/command/py37compat.py | 31 + .../setuptools/_distutils/command/register.py | 319 + .../setuptools/_distutils/command/sdist.py | 531 + .../setuptools/_distutils/command/upload.py | 205 + .../setuptools/_distutils/config.py | 139 + .../setuptools/_distutils/core.py | 291 + .../setuptools/_distutils/cygwinccompiler.py | 364 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 96 + .../setuptools/_distutils/dir_util.py | 243 + .../setuptools/_distutils/dist.py | 1286 + .../setuptools/_distutils/errors.py | 127 + .../setuptools/_distutils/extension.py | 248 + .../setuptools/_distutils/fancy_getopt.py | 470 + .../setuptools/_distutils/file_util.py | 249 + .../setuptools/_distutils/filelist.py | 371 + .../setuptools/_distutils/log.py | 80 + .../setuptools/_distutils/msvc9compiler.py | 832 + .../setuptools/_distutils/msvccompiler.py | 695 + .../setuptools/_distutils/py38compat.py | 8 + .../setuptools/_distutils/py39compat.py | 22 + .../setuptools/_distutils/spawn.py | 109 + .../setuptools/_distutils/sysconfig.py | 558 + .../setuptools/_distutils/text_file.py | 287 + .../setuptools/_distutils/unixccompiler.py | 401 + .../setuptools/_distutils/util.py | 513 + .../setuptools/_distutils/version.py | 358 + .../setuptools/_distutils/versionpredicate.py | 175 + .../site-packages/setuptools/_entry_points.py | 86 + .../site-packages/setuptools/_imp.py | 82 + .../site-packages/setuptools/_importlib.py | 47 + .../site-packages/setuptools/_itertools.py | 23 + .../site-packages/setuptools/_path.py | 29 + .../site-packages/setuptools/_reqs.py | 19 + .../setuptools/_vendor/__init__.py | 0 .../_vendor/importlib_metadata/__init__.py | 1047 + .../_vendor/importlib_metadata/_adapters.py | 68 + .../importlib_metadata/_collections.py | 30 + .../_vendor/importlib_metadata/_compat.py | 71 + .../_vendor/importlib_metadata/_functools.py | 104 + .../_vendor/importlib_metadata/_itertools.py | 73 + .../_vendor/importlib_metadata/_meta.py | 48 + .../_vendor/importlib_metadata/_text.py | 99 + .../_vendor/importlib_resources/__init__.py | 36 + .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 104 + .../_vendor/importlib_resources/_compat.py | 98 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 121 + .../_vendor/importlib_resources/abc.py | 137 + .../_vendor/importlib_resources/readers.py | 122 + .../_vendor/importlib_resources/simple.py | 116 + .../setuptools/_vendor/jaraco/__init__.py | 0 .../setuptools/_vendor/jaraco/context.py | 213 + .../setuptools/_vendor/jaraco/functools.py | 525 + .../_vendor/jaraco/text/__init__.py | 599 + .../_vendor/more_itertools/__init__.py | 4 + .../setuptools/_vendor/more_itertools/more.py | 3824 +++ .../_vendor/more_itertools/recipes.py | 620 + .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 26 + .../setuptools/_vendor/packaging/__init__.py | 25 + .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 61 + .../setuptools/_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 802 + .../setuptools/_vendor/packaging/tags.py | 487 + .../setuptools/_vendor/packaging/utils.py | 136 + .../setuptools/_vendor/packaging/version.py | 504 + .../setuptools/_vendor/pyparsing/__init__.py | 331 + .../setuptools/_vendor/pyparsing/actions.py | 207 + .../setuptools/_vendor/pyparsing/common.py | 424 + .../setuptools/_vendor/pyparsing/core.py | 5814 ++++ .../_vendor/pyparsing/diagram/__init__.py | 642 + .../_vendor/pyparsing/exceptions.py | 267 + .../setuptools/_vendor/pyparsing/helpers.py | 1088 + .../setuptools/_vendor/pyparsing/results.py | 760 + .../setuptools/_vendor/pyparsing/testing.py | 331 + .../setuptools/_vendor/pyparsing/unicode.py | 352 + .../setuptools/_vendor/pyparsing/util.py | 235 + .../setuptools/_vendor/tomli/__init__.py | 11 + .../setuptools/_vendor/tomli/_parser.py | 691 + .../setuptools/_vendor/tomli/_re.py | 107 + .../setuptools/_vendor/tomli/_types.py | 10 + .../setuptools/_vendor/typing_extensions.py | 2296 ++ .../site-packages/setuptools/_vendor/zipp.py | 329 + .../site-packages/setuptools/archive_util.py | 213 + .../site-packages/setuptools/build_meta.py | 511 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli-arm64.exe | Bin 0 -> 137216 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 12 + .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 457 + .../setuptools/command/bdist_rpm.py | 40 + .../site-packages/setuptools/command/build.py | 146 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 383 + .../setuptools/command/build_py.py | 368 + .../setuptools/command/develop.py | 193 + .../setuptools/command/dist_info.py | 142 + .../setuptools/command/easy_install.py | 2312 ++ .../setuptools/command/editable_wheel.py | 844 + .../setuptools/command/egg_info.py | 763 + .../setuptools/command/install.py | 139 + .../setuptools/command/install_egg_info.py | 63 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 70 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 210 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 251 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 213 + .../setuptools/config/__init__.py | 35 + .../setuptools/config/_apply_pyprojecttoml.py | 377 + .../config/_validate_pyproject/__init__.py | 34 + .../_validate_pyproject/error_reporting.py | 318 + .../_validate_pyproject/extra_validations.py | 36 + .../fastjsonschema_exceptions.py | 51 + .../fastjsonschema_validations.py | 1035 + .../config/_validate_pyproject/formats.py | 259 + .../site-packages/setuptools/config/expand.py | 462 + .../setuptools/config/pyprojecttoml.py | 493 + .../setuptools/config/setupcfg.py | 762 + .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/discovery.py | 600 + .../site-packages/setuptools/dist.py | 1222 + .../site-packages/setuptools/errors.py | 58 + .../site-packages/setuptools/extension.py | 148 + .../setuptools/extern/__init__.py | 76 + .../site-packages/setuptools/glob.py | 167 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui-arm64.exe | Bin 0 -> 137728 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 104 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/logging.py | 36 + .../site-packages/setuptools/monkey.py | 165 + .../site-packages/setuptools/msvc.py | 1703 ++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1126 + .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 530 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 42 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 222 + .../setuptools/windows_support.py | 29 + .../six-1.16.0.dist-info/INSTALLER | 1 + .../six-1.16.0.dist-info/LICENSE | 18 + .../six-1.16.0.dist-info/METADATA | 49 + .../site-packages/six-1.16.0.dist-info/RECORD | 9 + .../six-1.16.0.dist-info/REQUESTED | 0 .../site-packages/six-1.16.0.dist-info/WHEEL | 6 + .../six-1.16.0.dist-info/top_level.txt | 1 + .venv/lib/python3.11/site-packages/six.py | 998 + .../soupsieve-2.8.1.dist-info/INSTALLER | 1 + .../soupsieve-2.8.1.dist-info/METADATA | 115 + .../soupsieve-2.8.1.dist-info/RECORD | 20 + .../soupsieve-2.8.1.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 21 + .../site-packages/soupsieve/__init__.py | 168 + .../site-packages/soupsieve/__meta__.py | 197 + .../site-packages/soupsieve/css_match.py | 1645 + .../site-packages/soupsieve/css_parser.py | 1318 + .../site-packages/soupsieve/css_types.py | 407 + .../site-packages/soupsieve/pretty.py | 139 + .../site-packages/soupsieve/py.typed | 0 .../site-packages/soupsieve/util.py | 117 + .../tomli-2.0.1.dist-info/INSTALLER | 1 + .../tomli-2.0.1.dist-info/LICENSE | 21 + .../tomli-2.0.1.dist-info/METADATA | 206 + .../tomli-2.0.1.dist-info/RECORD | 15 + .../tomli-2.0.1.dist-info/REQUESTED | 0 .../site-packages/tomli-2.0.1.dist-info/WHEEL | 4 + .../site-packages/tomli/__init__.py | 11 + .../python3.11/site-packages/tomli/_parser.py | 691 + .../lib/python3.11/site-packages/tomli/_re.py | 107 + .../python3.11/site-packages/tomli/_types.py | 10 + .../python3.11/site-packages/tomli/py.typed | 1 + .../twine-4.0.2.dist-info/INSTALLER | 1 + .../twine-4.0.2.dist-info/LICENSE | 174 + .../twine-4.0.2.dist-info/METADATA | 85 + .../twine-4.0.2.dist-info/RECORD | 40 + .../twine-4.0.2.dist-info/REQUESTED | 0 .../site-packages/twine-4.0.2.dist-info/WHEEL | 5 + .../twine-4.0.2.dist-info/entry_points.txt | 7 + .../twine-4.0.2.dist-info/top_level.txt | 1 + .../site-packages/twine/__init__.py | 43 + .../site-packages/twine/__main__.py | 51 + .../python3.11/site-packages/twine/auth.py | 101 + .../lib/python3.11/site-packages/twine/cli.py | 123 + .../site-packages/twine/commands/__init__.py | 53 + .../site-packages/twine/commands/check.py | 183 + .../site-packages/twine/commands/register.py | 86 + .../site-packages/twine/commands/upload.py | 198 + .../site-packages/twine/exceptions.py | 124 + .../python3.11/site-packages/twine/package.py | 316 + .../python3.11/site-packages/twine/py.typed | 0 .../site-packages/twine/repository.py | 250 + .../site-packages/twine/settings.py | 335 + .../python3.11/site-packages/twine/utils.py | 324 + .../python3.11/site-packages/twine/wheel.py | 91 + .../python3.11/site-packages/twine/wininst.py | 61 + .../INSTALLER | 1 + .../METADATA | 72 + .../typing_extensions-4.15.0.dist-info/RECORD | 7 + .../typing_extensions-4.15.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .../site-packages/typing_extensions.py | 4317 +++ .../urllib3-2.6.3.dist-info/INSTALLER | 1 + .../urllib3-2.6.3.dist-info/METADATA | 164 + .../urllib3-2.6.3.dist-info/RECORD | 79 + .../urllib3-2.6.3.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 21 + .../site-packages/urllib3/__init__.py | 211 + .../site-packages/urllib3/_base_connection.py | 165 + .../site-packages/urllib3/_collections.py | 487 + .../site-packages/urllib3/_request_methods.py | 278 + .../site-packages/urllib3/_version.py | 34 + .../site-packages/urllib3/connection.py | 1099 + .../site-packages/urllib3/connectionpool.py | 1178 + .../site-packages/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/emscripten/__init__.py | 17 + .../urllib3/contrib/emscripten/connection.py | 260 + .../emscripten/emscripten_fetch_worker.js | 110 + .../urllib3/contrib/emscripten/fetch.py | 726 + .../urllib3/contrib/emscripten/request.py | 22 + .../urllib3/contrib/emscripten/response.py | 277 + .../urllib3/contrib/pyopenssl.py | 564 + .../site-packages/urllib3/contrib/socks.py | 228 + .../site-packages/urllib3/exceptions.py | 335 + .../site-packages/urllib3/fields.py | 341 + .../site-packages/urllib3/filepost.py | 89 + .../site-packages/urllib3/http2/__init__.py | 53 + .../site-packages/urllib3/http2/connection.py | 356 + .../site-packages/urllib3/http2/probe.py | 87 + .../site-packages/urllib3/poolmanager.py | 651 + .../python3.11/site-packages/urllib3/py.typed | 2 + .../site-packages/urllib3/response.py | 1480 + .../site-packages/urllib3/util/__init__.py | 42 + .../site-packages/urllib3/util/connection.py | 137 + .../site-packages/urllib3/util/proxy.py | 43 + .../site-packages/urllib3/util/request.py | 263 + .../site-packages/urllib3/util/response.py | 101 + .../site-packages/urllib3/util/retry.py | 549 + .../site-packages/urllib3/util/ssl_.py | 527 + .../urllib3/util/ssl_match_hostname.py | 159 + .../urllib3/util/ssltransport.py | 271 + .../site-packages/urllib3/util/timeout.py | 275 + .../site-packages/urllib3/util/url.py | 469 + .../site-packages/urllib3/util/util.py | 42 + .../site-packages/urllib3/util/wait.py | 124 + .../DESCRIPTION.rst | 27 + .../webencodings-0.5.1.dist-info/INSTALLER | 1 + .../webencodings-0.5.1.dist-info/METADATA | 52 + .../webencodings-0.5.1.dist-info/RECORD | 18 + .../webencodings-0.5.1.dist-info/REQUESTED | 0 .../webencodings-0.5.1.dist-info/WHEEL | 6 + .../metadata.json | 1 + .../top_level.txt | 1 + .../site-packages/webencodings/__init__.py | 342 + .../site-packages/webencodings/labels.py | 231 + .../site-packages/webencodings/mklabels.py | 59 + .../site-packages/webencodings/tests.py | 153 + .../webencodings/x_user_defined.py | 325 + .../site-packages/xdist/__init__.py | 15 + .../python3.11/site-packages/xdist/_path.py | 19 + .../site-packages/xdist/_version.py | 4 + .../site-packages/xdist/dsession.py | 459 + .../site-packages/xdist/looponfail.py | 284 + .../site-packages/xdist/newhooks.py | 95 + .../python3.11/site-packages/xdist/plugin.py | 342 + .../python3.11/site-packages/xdist/remote.py | 342 + .../python3.11/site-packages/xdist/report.py | 21 + .../site-packages/xdist/scheduler/__init__.py | 6 + .../site-packages/xdist/scheduler/each.py | 139 + .../site-packages/xdist/scheduler/load.py | 315 + .../site-packages/xdist/scheduler/loadfile.py | 52 + .../xdist/scheduler/loadgroup.py | 54 + .../xdist/scheduler/loadscope.py | 410 + .../xdist/scheduler/worksteal.py | 325 + .../site-packages/xdist/workermanage.py | 450 + .../python3.11/site-packages/yaml/__init__.py | 390 + .../_yaml.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 2650176 bytes .../python3.11/site-packages/yaml/composer.py | 139 + .../site-packages/yaml/constructor.py | 748 + .../python3.11/site-packages/yaml/cyaml.py | 101 + .../python3.11/site-packages/yaml/dumper.py | 62 + .../python3.11/site-packages/yaml/emitter.py | 1137 + .../python3.11/site-packages/yaml/error.py | 75 + .../python3.11/site-packages/yaml/events.py | 86 + .../python3.11/site-packages/yaml/loader.py | 63 + .../python3.11/site-packages/yaml/nodes.py | 49 + .../python3.11/site-packages/yaml/parser.py | 589 + .../python3.11/site-packages/yaml/reader.py | 185 + .../site-packages/yaml/representer.py | 389 + .../python3.11/site-packages/yaml/resolver.py | 227 + .../python3.11/site-packages/yaml/scanner.py | 1435 + .../site-packages/yaml/serializer.py | 111 + .../python3.11/site-packages/yaml/tokens.py | 104 + .../zipp-3.14.0.dist-info/INSTALLER | 1 + .../zipp-3.14.0.dist-info/LICENSE | 19 + .../zipp-3.14.0.dist-info/METADATA | 106 + .../zipp-3.14.0.dist-info/RECORD | 11 + .../zipp-3.14.0.dist-info/REQUESTED | 0 .../site-packages/zipp-3.14.0.dist-info/WHEEL | 5 + .../zipp-3.14.0.dist-info/top_level.txt | 1 + .../python3.11/site-packages/zipp/__init__.py | 397 + .../site-packages/zipp/py310compat.py | 12 + 2976 files changed, 832707 insertions(+) create mode 100644 .venv/bin/Activate.ps1 create mode 100755 .venv/bin/about create mode 100644 .venv/bin/activate create mode 100644 .venv/bin/activate.csh create mode 100644 .venv/bin/activate.fish create mode 100755 .venv/bin/black create mode 100755 .venv/bin/blackd create mode 100755 .venv/bin/docutils create mode 100755 .venv/bin/isort create mode 100755 .venv/bin/isort-identify-imports create mode 100755 .venv/bin/keyring create mode 100755 .venv/bin/markdown-it create mode 100755 .venv/bin/normalizer create mode 100755 .venv/bin/pip create mode 100755 .venv/bin/pip3 create mode 100755 .venv/bin/pip3.11 create mode 100755 .venv/bin/pkginfo create mode 100755 .venv/bin/py.test create mode 100755 .venv/bin/pycodestyle create mode 100755 .venv/bin/pygmentize create mode 100755 .venv/bin/pytest create mode 120000 .venv/bin/python create mode 120000 .venv/bin/python3 create mode 120000 .venv/bin/python3.11 create mode 100755 .venv/bin/rst2html.py create mode 100755 .venv/bin/rst2html4.py create mode 100755 .venv/bin/rst2html5.py create mode 100755 .venv/bin/rst2latex.py create mode 100755 .venv/bin/rst2man.py create mode 100755 .venv/bin/rst2odt.py create mode 100755 .venv/bin/rst2odt_prepstyles.py create mode 100755 .venv/bin/rst2pseudoxml.py create mode 100755 .venv/bin/rst2s5.py create mode 100755 .venv/bin/rst2xetex.py create mode 100755 .venv/bin/rst2xml.py create mode 100755 .venv/bin/rstpep2html.py create mode 100755 .venv/bin/twine create mode 100755 .venv/lib/python3.11/site-packages/6b397dd64e00b5aff23d__mypyc.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/AUTHORS create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/_black_version.py create mode 100755 .venv/lib/python3.11/site-packages/_cffi_backend.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/_distutils_hack/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_distutils_hack/override.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_argcomplete.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_code/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_code/code.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_code/source.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_io/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_io/pprint.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_io/saferepr.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_io/terminalwriter.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_io/wcwidth.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_py/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_py/error.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_py/path.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/_version.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/assertion/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/assertion/rewrite.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/assertion/truncate.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/assertion/util.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/cacheprovider.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/capture.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/compat.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/config/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/config/argparsing.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/config/compat.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/config/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/config/findpaths.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/debugging.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/deprecated.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/doctest.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/faulthandler.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/fixtures.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/freeze_support.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/helpconfig.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/hookspec.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/junitxml.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/legacypath.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/logging.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/main.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/mark/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/mark/expression.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/mark/structures.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/monkeypatch.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/nodes.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/outcomes.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/pastebin.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/pathlib.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/py.typed create mode 100644 .venv/lib/python3.11/site-packages/_pytest/pytester.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/pytester_assertions.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/python.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/python_api.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/raises.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/recwarn.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/reports.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/runner.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/scope.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/setuponly.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/setupplan.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/skipping.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/stash.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/stepwise.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/terminal.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/threadexception.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/timing.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/tmpdir.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/tracemalloc.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/unittest.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/unraisableexception.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/warning_types.py create mode 100644 .venv/lib/python3.11/site-packages/_pytest/warnings.py create mode 100644 .venv/lib/python3.11/site-packages/_yaml/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CHANGELOG.rst create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CODE_OF_CONDUCT.rst create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/NOTICE create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/apache-2.0.LICENSE create mode 100644 .venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/attr/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/attr/__init__.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/_cmp.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_cmp.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_config.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_funcs.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_make.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_next_gen.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_typing_compat.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/_version_info.py create mode 100644 .venv/lib/python3.11/site-packages/attr/_version_info.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/converters.py create mode 100644 .venv/lib/python3.11/site-packages/attr/converters.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/attr/exceptions.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/filters.py create mode 100644 .venv/lib/python3.11/site-packages/attr/filters.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/py.typed create mode 100644 .venv/lib/python3.11/site-packages/attr/setters.py create mode 100644 .venv/lib/python3.11/site-packages/attr/setters.pyi create mode 100644 .venv/lib/python3.11/site-packages/attr/validators.py create mode 100644 .venv/lib/python3.11/site-packages/attr/validators.pyi create mode 100644 .venv/lib/python3.11/site-packages/attributecode/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/api.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/attrib.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/attrib_util.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/cmd.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/gen.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/licenses.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/model.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/templates/default_html.template create mode 100644 .venv/lib/python3.11/site-packages/attributecode/templates/default_json.template create mode 100644 .venv/lib/python3.11/site-packages/attributecode/templates/license_ref.template create mode 100644 .venv/lib/python3.11/site-packages/attributecode/templates/list.csv create mode 100644 .venv/lib/python3.11/site-packages/attributecode/templates/scancode_html.template create mode 100644 .venv/lib/python3.11/site-packages/attributecode/transform.py create mode 100644 .venv/lib/python3.11/site-packages/attributecode/util.py create mode 100644 .venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/attrs/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/attrs/__init__.pyi create mode 100644 .venv/lib/python3.11/site-packages/attrs/converters.py create mode 100644 .venv/lib/python3.11/site-packages/attrs/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/attrs/filters.py create mode 100644 .venv/lib/python3.11/site-packages/attrs/py.typed create mode 100644 .venv/lib/python3.11/site-packages/attrs/setters.py create mode 100644 .venv/lib/python3.11/site-packages/attrs/validators.py create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS create mode 100644 .venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/AUTHORS.md create mode 100644 .venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/LICENSE create mode 100755 .venv/lib/python3.11/site-packages/black/__init__.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/black/__main__.py create mode 100755 .venv/lib/python3.11/site-packages/black/brackets.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/brackets.py create mode 100755 .venv/lib/python3.11/site-packages/black/cache.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/cache.py create mode 100755 .venv/lib/python3.11/site-packages/black/comments.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/comments.py create mode 100644 .venv/lib/python3.11/site-packages/black/concurrency.py create mode 100755 .venv/lib/python3.11/site-packages/black/const.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/const.py create mode 100644 .venv/lib/python3.11/site-packages/black/debug.py create mode 100644 .venv/lib/python3.11/site-packages/black/files.py create mode 100755 .venv/lib/python3.11/site-packages/black/handle_ipynb_magics.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/handle_ipynb_magics.py create mode 100755 .venv/lib/python3.11/site-packages/black/linegen.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/linegen.py create mode 100755 .venv/lib/python3.11/site-packages/black/lines.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/lines.py create mode 100755 .venv/lib/python3.11/site-packages/black/mode.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/mode.py create mode 100755 .venv/lib/python3.11/site-packages/black/nodes.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/nodes.py create mode 100755 .venv/lib/python3.11/site-packages/black/numerics.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/numerics.py create mode 100644 .venv/lib/python3.11/site-packages/black/output.py create mode 100755 .venv/lib/python3.11/site-packages/black/parsing.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/parsing.py create mode 100644 .venv/lib/python3.11/site-packages/black/py.typed create mode 100644 .venv/lib/python3.11/site-packages/black/report.py create mode 100755 .venv/lib/python3.11/site-packages/black/rusty.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/rusty.py create mode 100755 .venv/lib/python3.11/site-packages/black/strings.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/strings.py create mode 100755 .venv/lib/python3.11/site-packages/black/trans.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/black/trans.py create mode 100644 .venv/lib/python3.11/site-packages/blackd/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/blackd/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/blackd/middlewares.py create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/bleach/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/README.rst create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_ihatexml.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_inputstream.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_tokenizer.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_trie/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_trie/_base.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_trie/py.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/_utils.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/constants.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/base.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/lint.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/optionaltags.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/sanitizer.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/filters/whitespace.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/html5parser.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/serializer.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treeadapters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treeadapters/genshi.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treeadapters/sax.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treebuilders/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treebuilders/base.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treebuilders/dom.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treebuilders/etree.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/base.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/dom.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/etree.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/html5lib/treewalkers/genshi.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/parse.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/parse.py.SHA256SUM create mode 100644 .venv/lib/python3.11/site-packages/bleach/_vendor/vendor.txt create mode 100755 .venv/lib/python3.11/site-packages/bleach/_vendor/vendor_install.sh create mode 100644 .venv/lib/python3.11/site-packages/bleach/callbacks.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/css_sanitizer.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/html5lib_shim.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/linkifier.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/parse_shim.py create mode 100644 .venv/lib/python3.11/site-packages/bleach/sanitizer.py create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/Grammar.txt create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/PatternGrammar.txt create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/README create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/__init__.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/conv.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/conv.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/driver.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/driver.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/grammar.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/grammar.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/literals.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/literals.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/parse.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/parse.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/pgen.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/pgen.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/token.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/token.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pgen2/tokenize.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pgen2/tokenize.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pygram.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pygram.py create mode 100755 .venv/lib/python3.11/site-packages/blib2to3/pytree.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/blib2to3/pytree.py create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/CHANGELOG.rst create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/README.rst create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/boolean.py-4.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/boolean/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/boolean/boolean.py create mode 100644 .venv/lib/python3.11/site-packages/boolean/test_boolean.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/_deprecation.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/_typing.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/_warnings.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/builder/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/builder/_html5lib.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/builder/_htmlparser.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/builder/_lxml.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/css.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/dammit.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/diagnose.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/element.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/filter.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/formatter.py create mode 100644 .venv/lib/python3.11/site-packages/bs4/py.typed create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/certifi-2026.1.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/certifi/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/certifi/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/certifi/cacert.pem create mode 100644 .venv/lib/python3.11/site-packages/certifi/core.py create mode 100644 .venv/lib/python3.11/site-packages/certifi/py.typed create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/cffi-2.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/cffi/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/_cffi_errors.h create mode 100644 .venv/lib/python3.11/site-packages/cffi/_cffi_include.h create mode 100644 .venv/lib/python3.11/site-packages/cffi/_embedding.h create mode 100644 .venv/lib/python3.11/site-packages/cffi/_imp_emulation.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/_shimmed_dist_utils.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/api.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/backend_ctypes.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/cffi_opcode.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/commontypes.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/cparser.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/error.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/ffiplatform.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/lock.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/model.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/parse_c_type.h create mode 100644 .venv/lib/python3.11/site-packages/cffi/pkgconfig.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/recompiler.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/setuptools_ext.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/vengine_cpy.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/vengine_gen.py create mode 100644 .venv/lib/python3.11/site-packages/cffi/verifier.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/api.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/cd.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/cli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/cli/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/constant.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/legacy.py create mode 100755 .venv/lib/python3.11/site-packages/charset_normalizer/md.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/md.py create mode 100755 .venv/lib/python3.11/site-packages/charset_normalizer/md__mypyc.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/models.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/py.typed create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/utils.py create mode 100644 .venv/lib/python3.11/site-packages/charset_normalizer/version.py create mode 100644 .venv/lib/python3.11/site-packages/click-8.3.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/click-8.3.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/click-8.3.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/click-8.3.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/click-8.3.1.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.11/site-packages/click/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/click/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/click/_termui_impl.py create mode 100644 .venv/lib/python3.11/site-packages/click/_textwrap.py create mode 100644 .venv/lib/python3.11/site-packages/click/_utils.py create mode 100644 .venv/lib/python3.11/site-packages/click/_winconsole.py create mode 100644 .venv/lib/python3.11/site-packages/click/core.py create mode 100644 .venv/lib/python3.11/site-packages/click/decorators.py create mode 100644 .venv/lib/python3.11/site-packages/click/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/click/formatting.py create mode 100644 .venv/lib/python3.11/site-packages/click/globals.py create mode 100644 .venv/lib/python3.11/site-packages/click/parser.py create mode 100644 .venv/lib/python3.11/site-packages/click/py.typed create mode 100644 .venv/lib/python3.11/site-packages/click/shell_completion.py create mode 100644 .venv/lib/python3.11/site-packages/click/termui.py create mode 100644 .venv/lib/python3.11/site-packages/click/testing.py create mode 100644 .venv/lib/python3.11/site-packages/click/types.py create mode 100644 .venv/lib/python3.11/site-packages/click/utils.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/LICENSE.APACHE create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/LICENSE.BSD create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/LICENSE.PSF create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/cryptography-39.0.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/cryptography/__about__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/fernet.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/_oid.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/aead.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/backend.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/ciphers.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/cmac.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/dh.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/dsa.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/ec.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/ed25519.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/ed448.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/hashes.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/hmac.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/poly1305.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/rsa.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/utils.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/x25519.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/backends/openssl/x448.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/__init__.py create mode 100755 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_openssl.abi3.so create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_openssl.pyi create mode 100755 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust.abi3.so create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/openssl/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/bindings/openssl/binding.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/_asymmetric.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/_serialization.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/types.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/aead.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/base.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/ciphers/modes.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/cmac.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/constant_time.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/hashes.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/hmac.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/keywrap.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/padding.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/poly1305.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/serialization/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/serialization/base.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/serialization/ssh.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/hazmat/primitives/twofactor/totp.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/py.typed create mode 100644 .venv/lib/python3.11/site-packages/cryptography/utils.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/base.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/certificate_transparency.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/extensions.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/general_name.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/name.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/ocsp.py create mode 100644 .venv/lib/python3.11/site-packages/cryptography/x509/oid.py create mode 100644 .venv/lib/python3.11/site-packages/distutils-precedence.pth create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/COPYING.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/SOURCES.html create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/dependency_links.html create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/top_level.html create mode 100644 .venv/lib/python3.11/site-packages/docutils-0.19.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/core.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/examples.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/frontend.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/io.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/af.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/ar.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/ca.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/cs.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/da.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/de.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/en.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/eo.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/es.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/fa.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/fi.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/fr.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/gl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/he.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/it.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/ja.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/ko.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/lt.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/lv.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/nl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/pl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/pt_br.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/ru.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/sk.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/sv.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/zh_cn.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/languages/zh_tw.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/nodes.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/commonmark_wrapper.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/null.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/recommonmark_wrapper.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/admonitions.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/body.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/html.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/images.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/misc.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/parts.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/references.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/directives/tables.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/README.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsa.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsb.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsc.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsn.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamso.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isoamsr.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isobox.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isocyr1.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isocyr2.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isodia.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk1.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk2.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk3.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk4-wide.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isogrk4.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isolat1.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isolat2.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomfrk-wide.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomfrk.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomopf-wide.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomopf.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomscr-wide.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isomscr.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isonum.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isopub.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/isotech.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlalias.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlextra-wide.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/mmlextra.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/s5defs.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-lat1.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-special.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/include/xhtml1-symbol.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/af.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ar.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ca.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/cs.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/da.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/de.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/en.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/eo.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/es.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fa.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fi.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/fr.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/gl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/he.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/it.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ja.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ko.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/lt.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/lv.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/nl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/pl.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/pt_br.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/ru.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/sk.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/sv.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/zh_cn.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/languages/zh_tw.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/roles.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/states.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/parsers/rst/tableparser.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/readers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/readers/doctree.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/readers/pep.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/readers/standalone.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/statemachine.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/components.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/frontmatter.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/misc.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/parts.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/peps.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/references.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/universal.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/transforms/writer_aux.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/code_analyzer.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/error_reporting.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/latex2mathml.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/math2html.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/tex2mathml_extern.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/tex2unichar.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/math/unichar2tex.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/punctuation_chars.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/roman.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/smartquotes.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/utils/urischemes.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/_html_base.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/docutils_xml.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html4css1/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html4css1/html4css1.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html4css1/template.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/math.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/minimal.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/plain.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/responsive.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/template.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/html5_polyglot/tuftig.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/default.tex create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/docutils.sty create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/titlepage.tex create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/titlingpage.tex create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/latex2e/xelatex.tex create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/manpage.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/null.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/odf_odt/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/odf_odt/pygmentsformatter.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/odf_odt/styles.odt create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/pep_html/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/pep_html/pep.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/pep_html/template.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/pseudoxml.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/README.txt create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/__base__ create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/framing.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-black/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-white/framing.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/big-white/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/framing.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/opera.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/outline.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/print.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/s5-core.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/slides.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/default/slides.js create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-black/__base__ create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-black/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-white/framing.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/medium-white/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-black/__base__ create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-black/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-white/framing.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/s5_html/themes/small-white/pretty.css create mode 100644 .venv/lib/python3.11/site-packages/docutils/writers/xetex/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/et_xmlfile/xmlfile.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup-1.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/_catch.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/_exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/_formatting.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/_version.py create mode 100644 .venv/lib/python3.11/site-packages/exceptiongroup/py.typed create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/execnet-1.9.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/execnet/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/_version.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/deprecated.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/gateway.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/gateway_base.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/gateway_bootstrap.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/gateway_io.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/gateway_socket.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/multi.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/rsync.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/rsync_remote.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/loop_socketserver.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/quitserver.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/shell.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/socketserver.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/socketserverservice.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/script/xx.py create mode 100644 .venv/lib/python3.11/site-packages/execnet/xspec.py create mode 100644 .venv/lib/python3.11/site-packages/idna-3.11.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/idna-3.11.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/idna-3.11.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/idna-3.11.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/idna-3.11.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.11/site-packages/idna/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/idna/codec.py create mode 100644 .venv/lib/python3.11/site-packages/idna/compat.py create mode 100644 .venv/lib/python3.11/site-packages/idna/core.py create mode 100644 .venv/lib/python3.11/site-packages/idna/idnadata.py create mode 100644 .venv/lib/python3.11/site-packages/idna/intranges.py create mode 100644 .venv/lib/python3.11/site-packages/idna/package_data.py create mode 100644 .venv/lib/python3.11/site-packages/idna/py.typed create mode 100644 .venv/lib/python3.11/site-packages/idna/uts46data.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata-6.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_adapters.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_collections.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_functools.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_itertools.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_meta.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_py39compat.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/_text.py create mode 100644 .venv/lib/python3.11/site-packages/importlib_metadata/py.typed create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/iniconfig/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/iniconfig/_parse.py create mode 100644 .venv/lib/python3.11/site-packages/iniconfig/_version.py create mode 100644 .venv/lib/python3.11/site-packages/iniconfig/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/iniconfig/py.typed create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/isort-6.0.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/isort/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/isort/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/isort/_vendored/tomli/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/isort/_vendored/tomli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/isort/_vendored/tomli/_parser.py create mode 100644 .venv/lib/python3.11/site-packages/isort/_vendored/tomli/_re.py create mode 100644 .venv/lib/python3.11/site-packages/isort/_vendored/tomli/py.typed create mode 100644 .venv/lib/python3.11/site-packages/isort/_version.py create mode 100644 .venv/lib/python3.11/site-packages/isort/api.py create mode 100644 .venv/lib/python3.11/site-packages/isort/comments.py create mode 100644 .venv/lib/python3.11/site-packages/isort/core.py create mode 100644 .venv/lib/python3.11/site-packages/isort/deprecated/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/isort/deprecated/finders.py create mode 100644 .venv/lib/python3.11/site-packages/isort/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/isort/files.py create mode 100644 .venv/lib/python3.11/site-packages/isort/format.py create mode 100644 .venv/lib/python3.11/site-packages/isort/hooks.py create mode 100644 .venv/lib/python3.11/site-packages/isort/identify.py create mode 100644 .venv/lib/python3.11/site-packages/isort/io.py create mode 100644 .venv/lib/python3.11/site-packages/isort/literal.py create mode 100644 .venv/lib/python3.11/site-packages/isort/logo.py create mode 100644 .venv/lib/python3.11/site-packages/isort/main.py create mode 100644 .venv/lib/python3.11/site-packages/isort/output.py create mode 100644 .venv/lib/python3.11/site-packages/isort/parse.py create mode 100644 .venv/lib/python3.11/site-packages/isort/place.py create mode 100644 .venv/lib/python3.11/site-packages/isort/profiles.py create mode 100644 .venv/lib/python3.11/site-packages/isort/py.typed create mode 100644 .venv/lib/python3.11/site-packages/isort/pylama_isort.py create mode 100644 .venv/lib/python3.11/site-packages/isort/sections.py create mode 100644 .venv/lib/python3.11/site-packages/isort/settings.py create mode 100644 .venv/lib/python3.11/site-packages/isort/setuptools_commands.py create mode 100644 .venv/lib/python3.11/site-packages/isort/sorting.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/all.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py2.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py27.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py3.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py310.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py311.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py312.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py313.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py36.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py37.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py38.py create mode 100644 .venv/lib/python3.11/site-packages/isort/stdlibs/py39.py create mode 100644 .venv/lib/python3.11/site-packages/isort/utils.py create mode 100644 .venv/lib/python3.11/site-packages/isort/wrap.py create mode 100644 .venv/lib/python3.11/site-packages/isort/wrap_modes.py create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/jaraco.classes-3.2.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/jaraco/classes/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jaraco/classes/ancestry.py create mode 100644 .venv/lib/python3.11/site-packages/jaraco/classes/meta.py create mode 100644 .venv/lib/python3.11/site-packages/jaraco/classes/properties.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/jeepney-0.8.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/jeepney/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/auth.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/bindgen.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/bus.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/bus_messages.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/fds.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/asyncio.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/blocking.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/common.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/conftest.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/test_asyncio.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/test_blocking.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/test_threading.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/test_trio.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/tests/utils.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/threading.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/io/trio.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/low_level.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/routing.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/secrets_introspect.xml create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_auth.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_bindgen.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_bus.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_bus_messages.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_fds.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_low_level.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/tests/test_routing.py create mode 100644 .venv/lib/python3.11/site-packages/jeepney/wrappers.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/_identifier.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/async_utils.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/bccache.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/compiler.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/constants.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/debug.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/defaults.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/environment.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/ext.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/filters.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/idtracking.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/lexer.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/loaders.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/meta.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/nativetypes.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/nodes.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/optimizer.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/parser.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/py.typed create mode 100644 .venv/lib/python3.11/site-packages/jinja2/runtime.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/sandbox.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/tests.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/utils.py create mode 100644 .venv/lib/python3.11/site-packages/jinja2/visitor.py create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/keyring-23.13.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/keyring/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/_properties_compat.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backend.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backend_complete.zsh create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/OS_X.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/SecretService.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/Windows.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/chainer.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/fail.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/kwallet.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/libsecret.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/macOS/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/macOS/api.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/backends/null.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/cli.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/completion.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/core.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/credentials.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/devpi_client.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/errors.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/http.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/py.typed create mode 100644 .venv/lib/python3.11/site-packages/keyring/py312compat.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/testing/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/testing/backend.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/testing/util.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/util/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/util/platform_.py create mode 100644 .venv/lib/python3.11/site-packages/keyring/util/properties.py create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/CHANGELOG.rst create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/CODE_OF_CONDUCT.rst create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/NOTICE create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/apache-2.0.LICENSE create mode 100644 .venv/lib/python3.11/site-packages/license_expression-30.1.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/license_expression/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/license_expression/_pyahocorasick.ABOUT create mode 100644 .venv/lib/python3.11/site-packages/license_expression/_pyahocorasick.py create mode 100644 .venv/lib/python3.11/site-packages/license_expression/data/cc-by-4.0.LICENSE create mode 100644 .venv/lib/python3.11/site-packages/license_expression/data/license_key_index.json.ABOUT create mode 100644 .venv/lib/python3.11/site-packages/license_expression/data/scancode-licensedb-index.json create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/_punycode.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/cli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/cli/parse.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/entities.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/html_blocks.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/html_re.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/normalize_url.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/common/utils.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/helpers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_destination.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_label.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/helpers/parse_link_title.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/main.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/parser_block.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/parser_core.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/parser_inline.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/port.yaml create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/presets/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/presets/commonmark.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/presets/default.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/presets/zero.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/py.typed create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/renderer.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/ruler.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/blockquote.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/code.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/fence.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/heading.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/hr.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/html_block.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/lheading.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/list.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/paragraph.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/reference.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/state_block.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_block/table.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/block.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/inline.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/linkify.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/normalize.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/replacements.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/smartquotes.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_core/state_core.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/autolink.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/backticks.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/balance_pairs.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/emphasis.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/entity.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/escape.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/html_inline.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/image.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/link.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/newline.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/state_inline.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/strikethrough.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/text.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/rules_inline/text_collapse.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/token.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/tree.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it/utils.py create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/LICENSE.markdown-it create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/markdown_it_py-2.2.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/markupsafe/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/markupsafe/_native.py create mode 100644 .venv/lib/python3.11/site-packages/markupsafe/_speedups.c create mode 100755 .venv/lib/python3.11/site-packages/markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/markupsafe/_speedups.pyi create mode 100644 .venv/lib/python3.11/site-packages/markupsafe/py.typed create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/mdurl-0.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/mdurl/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/_decode.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/_encode.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/_format.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/_parse.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/_url.py create mode 100644 .venv/lib/python3.11/site-packages/mdurl/py.typed create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/more_itertools-9.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/__init__.pyi create mode 100755 .venv/lib/python3.11/site-packages/more_itertools/more.py create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/more.pyi create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/py.typed create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/recipes.py create mode 100644 .venv/lib/python3.11/site-packages/more_itertools/recipes.pyi create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions-1.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/mypy_extensions.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/LICENCE.rst create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/openpyxl-3.1.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/_constants.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/_writer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/cell.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/read_only.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/rich_text.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/cell/text.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/_3d.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/area_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/axis.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/bar_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/bubble_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/chartspace.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/data_source.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/descriptors.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/error_bar.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/label.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/layout.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/legend.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/line_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/marker.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/picture.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/pie_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/pivot.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/plotarea.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/print_settings.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/radar_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/reader.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/reference.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/scatter_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/series.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/series_factory.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/shapes.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/stock_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/surface_chart.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/text.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/title.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/trendline.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chart/updown_bars.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/chartsheet.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/custom.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/properties.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/protection.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/publish.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/relation.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/chartsheet/views.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/comments/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/comments/author.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/comments/comment_sheet.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/comments/comments.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/comments/shape_writer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/abc.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/numbers.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/product.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/singleton.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/compat/strings.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/base.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/excel.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/namespace.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/nested.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/sequence.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/serialisable.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/descriptors/slots.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/colors.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/connector.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/drawing.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/effect.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/fill.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/geometry.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/graphic.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/image.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/line.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/picture.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/properties.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/relation.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/spreadsheet_drawing.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/text.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/drawing/xdr.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formatting/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formatting/formatting.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formatting/rule.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formula/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formula/tokenizer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/formula/translate.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/core.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/custom.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/extended.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/interface.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/manifest.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/relationship.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/packaging/workbook.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/pivot/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/pivot/cache.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/pivot/fields.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/pivot/record.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/pivot/table.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/reader/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/reader/drawings.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/reader/excel.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/reader/strings.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/reader/workbook.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/alignment.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/borders.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/builtins.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/cell_style.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/colors.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/differential.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/fills.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/fonts.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/named_styles.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/numbers.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/protection.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/proxy.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/styleable.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/stylesheet.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/styles/table.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/bound_dictionary.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/cell.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/dataframe.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/datetime.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/escape.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/formulas.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/indexed_list.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/inference.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/protection.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/utils/units.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/_writer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/child.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/defined_name.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/external_link/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/external_link/external.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/external_reference.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/function_group.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/properties.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/protection.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/smart_tags.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/views.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/web.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/workbook/workbook.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/_read_only.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/_reader.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/_write_only.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/_writer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/cell_range.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/cell_watch.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/controls.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/copier.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/custom.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/datavalidation.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/dimensions.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/drawing.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/errors.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/filters.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/formula.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/header_footer.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/hyperlink.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/merge.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/ole.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/page.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/pagebreak.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/picture.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/print_settings.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/properties.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/protection.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/related.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/scenario.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/smart_tag.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/table.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/views.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/worksheet/worksheet.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/writer/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/writer/excel.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/writer/theme.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/xml/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/xml/constants.py create mode 100644 .venv/lib/python3.11/site-packages/openpyxl/xml/functions.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/django/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/django/filters.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/django/models.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/django/utils.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/purl2url.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/route.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl/contrib/url2purl.py create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/CHANGELOG.rst create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/CONTRIBUTING.rst create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/README.rst create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/mit.LICENSE create mode 100644 .venv/lib/python3.11/site-packages/packageurl_python-0.10.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/LICENSE.APACHE create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/LICENSE.BSD create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/packaging-23.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/packaging/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_elffile.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_manylinux.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_musllinux.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_parser.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_structures.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/_tokenizer.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/markers.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/py.typed create mode 100644 .venv/lib/python3.11/site-packages/packaging/requirements.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/specifiers.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/tags.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/utils.py create mode 100644 .venv/lib/python3.11/site-packages/packaging/version.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pathspec-0.11.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pathspec/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/_meta.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/gitignore.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/pathspec.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/pattern.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/patterns/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/patterns/gitwildmatch.py create mode 100644 .venv/lib/python3.11/site-packages/pathspec/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pathspec/util.py create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/pip-24.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pip/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/__pip-runner__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/build_env.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/base_command.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/command_context.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/main.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/main_parser.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/parser.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/req_command.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/spinners.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/cli/status_codes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/check.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/completion.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/configuration.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/debug.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/download.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/freeze.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/hash.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/help.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/index.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/inspect.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/install.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/list.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/search.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/show.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/uninstall.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/commands/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/configuration.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/distributions/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/distributions/base.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/distributions/installed.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/distributions/sdist.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/distributions/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/index/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/index/collector.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/index/package_finder.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/index/sources.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/locations/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/locations/_distutils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/locations/base.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/main.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/_json.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/base.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/candidate.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/direct_url.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/format_control.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/index.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/installation_report.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/link.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/scheme.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/search_scope.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/target_python.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/models/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/auth.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/download.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/session.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/build_tracker.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/check.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/freeze.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/operations/prepare.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/pyproject.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/constructors.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/req_file.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/req_install.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/req_set.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/base.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/self_outdated_check.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/_jaraco_text.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/_log.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/appdirs.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/datetime.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/deprecation.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/egg_link.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/encoding.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/filesystem.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/filetypes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/glibc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/hashes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/logging.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/misc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/models.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/packaging.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/subprocess.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/unpacking.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/urls.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/utils/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/git.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/subversion.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_internal/wheel_builder.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/certifi/core.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/certifi/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/codingstatemachinedict.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/enums.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/johabfreq.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/johabprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/macromanprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/resultdict.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/utf1632prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/chardet/version.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/ansi_test.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/initialise_test.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/isatty_test.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/tests/winterm_test.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/win32.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/database.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/index.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/locators.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/markers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/resources.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/t64-arm.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/util.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/version.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/w64-arm.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distro/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distro/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distro/distro.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/distro/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/codec.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/core.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/intranges.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/package_data.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/console.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/filter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/style.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/token.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pygments/util.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/actions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/common.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/core.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/diagram/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/helpers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/results.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/testing.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/unicode.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/__version__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/adapters.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/api.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/auth.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/certs.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/compat.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/cookies.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/help.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/hooks.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/models.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/packages.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/sessions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/structures.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/requests/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_extension.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_fileno.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_loop.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_pick.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_stack.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_timer.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_windows.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/abc.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/align.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/ansi.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/bar.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/box.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/cells.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/color.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/columns.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/console.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/constrain.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/containers.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/control.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/emoji.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/errors.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/filesize.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/json.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/layout.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/live.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/live_render.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/logging.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/markup.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/measure.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/padding.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/pager.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/palette.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/panel.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/pretty.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/progress.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/prompt.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/protocol.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/region.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/repr.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/rule.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/scope.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/screen.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/segment.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/spinner.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/status.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/style.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/styled.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/syntax.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/table.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/text.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/theme.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/themes.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/traceback.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/rich/tree.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/six.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/after.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/before.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tomli/_types.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/tomli/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/_api.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/_macos.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/_openssl.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/_ssl_constants.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/_windows.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/truststore/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/typing_extensions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/request.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/response.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/vendor.txt create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 .venv/lib/python3.11/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 .venv/lib/python3.11/site-packages/pip/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_common.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/abc.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/readers.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/importlib_resources/simple.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/context.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/functools.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/more.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/more_itertools/recipes.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/actions.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/common.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/core.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/helpers.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/results.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/testing.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/unicode.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/pyparsing/util.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/_vendor/zipp.py create mode 100644 .venv/lib/python3.11/site-packages/pkg_resources/extern/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/pkginfo-1.9.6.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/__init__.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/bdist.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/bdist.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/commandline.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/commandline.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/develop.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/develop.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/distribution.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/distribution.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/index.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/index.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/installed.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/installed.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/sdist.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/sdist.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_bdist.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_commandline.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_develop.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_distribution.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_index.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_installed.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_sdist.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_utils.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/tests/test_wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/utils.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/utils.pyi create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/pkginfo/wheel.pyi create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/platformdirs-3.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/android.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/api.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/macos.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/py.typed create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/unix.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/version.py create mode 100644 .venv/lib/python3.11/site-packages/platformdirs/windows.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pluggy-1.6.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pluggy/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_callers.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_hooks.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_manager.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_result.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_tracing.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_version.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/_warnings.py create mode 100644 .venv/lib/python3.11/site-packages/pluggy/py.typed create mode 100644 .venv/lib/python3.11/site-packages/py.py create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle-2.13.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pycodestyle.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pycparser-2.21.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pycparser/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/_ast_gen.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/_build_tables.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/_c_ast.cfg create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ast_transforms.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/c_ast.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/c_generator.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/c_lexer.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/c_parser.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/lextab.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/cpp.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/ctokens.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/lex.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/yacc.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/ply/ygen.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/plyparser.py create mode 100644 .venv/lib/python3.11/site-packages/pycparser/yacctab.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/cmdline.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/console.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/filter.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/filters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatter.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/_mapping.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/bbcode.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/groff.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/html.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/img.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/irc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/latex.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/other.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/pangomarkup.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/rtf.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/svg.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/terminal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/formatters/terminal256.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexer.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_ada_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_asy_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_cl_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_cocoa_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_csound_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_css_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_julia_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_lasso_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_lilypond_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_lua_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_mapping.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_mql_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_mysql_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_openedge_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_php_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_postgres_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_qlik_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_scheme_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_scilab_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_sourcemod_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_stan_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_stata_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_tsql_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_usd_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_vbscript_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/_vim_builtins.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/actionscript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ada.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/agile.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/algebra.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ambient.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/amdgpu.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ampl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/apdlexer.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/apl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/archetype.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/arrow.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/arturo.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/asc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/asm.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/automation.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/bare.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/basic.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/bdd.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/berry.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/bibtex.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/boa.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/business.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/c_cpp.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/c_like.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/capnproto.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/cddl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/chapel.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/clean.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/comal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/compiled.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/configs.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/console.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/cplint.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/crystal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/csound.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/css.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/d.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/dalvik.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/data.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/devicetree.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/diff.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/dotnet.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/dsls.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/dylan.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ecl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/eiffel.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/elm.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/elpi.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/email.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/erlang.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/esoteric.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ezhil.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/factor.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/fantom.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/felix.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/fift.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/floscript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/forth.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/fortran.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/foxpro.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/freefem.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/func.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/functional.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/futhark.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/gcodelexer.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/gdscript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/go.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/grammar_notation.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/graph.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/graphics.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/graphviz.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/gsql.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/haskell.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/haxe.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/hdl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/hexdump.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/html.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/idl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/igor.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/inferno.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/installers.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/int_fiction.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/iolang.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/j.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/javascript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/jmespath.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/jslt.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/jsonnet.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/julia.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/jvm.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/kuin.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/lilypond.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/lisp.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/macaulay2.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/make.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/markup.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/math.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/matlab.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/maxima.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/meson.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/mime.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/minecraft.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/mips.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ml.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/modeling.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/modula2.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/monte.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/mosel.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ncl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/nimrod.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/nit.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/nix.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/oberon.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/objective.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ooc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/other.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/parasail.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/parsers.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/pascal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/pawn.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/perl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/phix.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/php.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/pointless.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/pony.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/praat.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/procfile.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/prolog.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/promql.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/python.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/q.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/qlik.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/qvt.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/r.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/rdf.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/rebol.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/resource.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ride.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/rita.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/rnc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/roboconf.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/robotframework.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ruby.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/rust.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/sas.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/savi.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/scdoc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/scripting.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/sgf.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/shell.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/sieve.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/slash.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/smalltalk.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/smithy.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/smv.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/snobol.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/solidity.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/sophia.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/special.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/spice.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/sql.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/srcinfo.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/stata.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/supercollider.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/tal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/tcl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/teal.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/templates.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/teraterm.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/testing.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/text.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/textedit.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/textfmts.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/theorem.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/thingsdb.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/tlb.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/tnt.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/trafficscript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/typoscript.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/ul4.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/unicon.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/urbi.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/usd.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/varnish.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/verification.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/web.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/webassembly.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/webidl.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/webmisc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/whiley.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/wowtoc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/wren.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/x10.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/xorg.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/yang.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/lexers/zig.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/modeline.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/plugin.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/regexopt.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/scanner.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/sphinxext.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/style.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/abap.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/algol.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/algol_nu.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/arduino.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/autumn.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/borland.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/bw.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/colorful.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/default.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/dracula.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/emacs.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/friendly.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/friendly_grayscale.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/fruity.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/gh_dark.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/gruvbox.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/igor.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/inkpot.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/lilypond.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/lovelace.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/manni.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/material.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/monokai.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/murphy.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/native.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/nord.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/onedark.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/paraiso_dark.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/paraiso_light.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/pastie.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/perldoc.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/rainbow_dash.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/rrt.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/sas.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/solarized.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/staroffice.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/stata_dark.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/stata_light.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/tango.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/trac.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/vim.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/vs.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/xcode.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/styles/zenburn.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/token.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/unistring.py create mode 100644 .venv/lib/python3.11/site-packages/pygments/util.py create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/licenses/AUTHORS create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pytest-8.4.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pytest/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/pytest/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/pytest/py.typed create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/pytest_xdist-3.2.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/pyyaml-6.0.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer-37.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/__about__.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/clean.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/markdown.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/py.typed create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/rst.py create mode 100644 .venv/lib/python3.11/site-packages/readme_renderer/txt.py create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/requests-2.32.5.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/requests/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests/__version__.py create mode 100644 .venv/lib/python3.11/site-packages/requests/_internal_utils.py create mode 100644 .venv/lib/python3.11/site-packages/requests/adapters.py create mode 100644 .venv/lib/python3.11/site-packages/requests/api.py create mode 100644 .venv/lib/python3.11/site-packages/requests/auth.py create mode 100644 .venv/lib/python3.11/site-packages/requests/certs.py create mode 100644 .venv/lib/python3.11/site-packages/requests/compat.py create mode 100644 .venv/lib/python3.11/site-packages/requests/cookies.py create mode 100644 .venv/lib/python3.11/site-packages/requests/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/requests/help.py create mode 100644 .venv/lib/python3.11/site-packages/requests/hooks.py create mode 100644 .venv/lib/python3.11/site-packages/requests/models.py create mode 100644 .venv/lib/python3.11/site-packages/requests/packages.py create mode 100644 .venv/lib/python3.11/site-packages/requests/sessions.py create mode 100644 .venv/lib/python3.11/site-packages/requests/status_codes.py create mode 100644 .venv/lib/python3.11/site-packages/requests/structures.py create mode 100644 .venv/lib/python3.11/site-packages/requests/utils.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt-0.10.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/appengine.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/fingerprint.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/host_header_ssl.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/socket_options.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/source.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/ssl.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/adapters/x509.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/auth/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/auth/_digest_auth_compat.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/auth/guess.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/auth/handler.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/auth/http_proxy_digest.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/cookies/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/cookies/forgetful.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/downloadutils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/downloadutils/stream.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/downloadutils/tee.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/multipart/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/multipart/decoder.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/multipart/encoder.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/sessions.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/streaming_iterator.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/threaded/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/threaded/pool.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/threaded/thread.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/utils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/utils/deprecated.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/utils/dump.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/utils/formdata.py create mode 100644 .venv/lib/python3.11/site-packages/requests_toolbelt/utils/user_agent.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/rfc3986-2.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/_mixin.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/abnf_regexp.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/api.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/builder.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/compat.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/iri.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/misc.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/normalizers.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/parseresult.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/uri.py create mode 100644 .venv/lib/python3.11/site-packages/rfc3986/validators.py create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/rich-13.3.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/rich/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/rich/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_cell_widths.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_emoji_codes.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_emoji_replace.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_export_format.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_extension.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_fileno.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_inspect.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_log_render.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_loop.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_null_file.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_palettes.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_pick.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_ratio.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_spinners.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_stack.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_timer.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_win32_console.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_windows.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_windows_renderer.py create mode 100644 .venv/lib/python3.11/site-packages/rich/_wrap.py create mode 100644 .venv/lib/python3.11/site-packages/rich/abc.py create mode 100644 .venv/lib/python3.11/site-packages/rich/align.py create mode 100644 .venv/lib/python3.11/site-packages/rich/ansi.py create mode 100644 .venv/lib/python3.11/site-packages/rich/bar.py create mode 100644 .venv/lib/python3.11/site-packages/rich/box.py create mode 100644 .venv/lib/python3.11/site-packages/rich/cells.py create mode 100644 .venv/lib/python3.11/site-packages/rich/color.py create mode 100644 .venv/lib/python3.11/site-packages/rich/color_triplet.py create mode 100644 .venv/lib/python3.11/site-packages/rich/columns.py create mode 100644 .venv/lib/python3.11/site-packages/rich/console.py create mode 100644 .venv/lib/python3.11/site-packages/rich/constrain.py create mode 100644 .venv/lib/python3.11/site-packages/rich/containers.py create mode 100644 .venv/lib/python3.11/site-packages/rich/control.py create mode 100644 .venv/lib/python3.11/site-packages/rich/default_styles.py create mode 100644 .venv/lib/python3.11/site-packages/rich/diagnose.py create mode 100644 .venv/lib/python3.11/site-packages/rich/emoji.py create mode 100644 .venv/lib/python3.11/site-packages/rich/errors.py create mode 100644 .venv/lib/python3.11/site-packages/rich/file_proxy.py create mode 100644 .venv/lib/python3.11/site-packages/rich/filesize.py create mode 100644 .venv/lib/python3.11/site-packages/rich/highlighter.py create mode 100644 .venv/lib/python3.11/site-packages/rich/json.py create mode 100644 .venv/lib/python3.11/site-packages/rich/jupyter.py create mode 100644 .venv/lib/python3.11/site-packages/rich/layout.py create mode 100644 .venv/lib/python3.11/site-packages/rich/live.py create mode 100644 .venv/lib/python3.11/site-packages/rich/live_render.py create mode 100644 .venv/lib/python3.11/site-packages/rich/logging.py create mode 100644 .venv/lib/python3.11/site-packages/rich/markdown.py create mode 100644 .venv/lib/python3.11/site-packages/rich/markup.py create mode 100644 .venv/lib/python3.11/site-packages/rich/measure.py create mode 100644 .venv/lib/python3.11/site-packages/rich/padding.py create mode 100644 .venv/lib/python3.11/site-packages/rich/pager.py create mode 100644 .venv/lib/python3.11/site-packages/rich/palette.py create mode 100644 .venv/lib/python3.11/site-packages/rich/panel.py create mode 100644 .venv/lib/python3.11/site-packages/rich/pretty.py create mode 100644 .venv/lib/python3.11/site-packages/rich/progress.py create mode 100644 .venv/lib/python3.11/site-packages/rich/progress_bar.py create mode 100644 .venv/lib/python3.11/site-packages/rich/prompt.py create mode 100644 .venv/lib/python3.11/site-packages/rich/protocol.py create mode 100644 .venv/lib/python3.11/site-packages/rich/py.typed create mode 100644 .venv/lib/python3.11/site-packages/rich/region.py create mode 100644 .venv/lib/python3.11/site-packages/rich/repr.py create mode 100644 .venv/lib/python3.11/site-packages/rich/rule.py create mode 100644 .venv/lib/python3.11/site-packages/rich/scope.py create mode 100644 .venv/lib/python3.11/site-packages/rich/screen.py create mode 100644 .venv/lib/python3.11/site-packages/rich/segment.py create mode 100644 .venv/lib/python3.11/site-packages/rich/spinner.py create mode 100644 .venv/lib/python3.11/site-packages/rich/status.py create mode 100644 .venv/lib/python3.11/site-packages/rich/style.py create mode 100644 .venv/lib/python3.11/site-packages/rich/styled.py create mode 100644 .venv/lib/python3.11/site-packages/rich/syntax.py create mode 100644 .venv/lib/python3.11/site-packages/rich/table.py create mode 100644 .venv/lib/python3.11/site-packages/rich/terminal_theme.py create mode 100644 .venv/lib/python3.11/site-packages/rich/text.py create mode 100644 .venv/lib/python3.11/site-packages/rich/theme.py create mode 100644 .venv/lib/python3.11/site-packages/rich/themes.py create mode 100644 .venv/lib/python3.11/site-packages/rich/traceback.py create mode 100644 .venv/lib/python3.11/site-packages/rich/tree.py create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/AUTHORS.rst create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/CHANGELOG.rst create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/CODE_OF_CONDUCT.rst create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/NOTICE create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/apache-2.0.LICENSE create mode 100644 .venv/lib/python3.11/site-packages/saneyaml-0.6.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/saneyaml.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/collection.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/defines.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/dhcrypto.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/item.py create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/py.typed create mode 100644 .venv/lib/python3.11/site-packages/secretstorage/util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/setuptools-65.5.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/setuptools/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_deprecation_warning.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/_collections.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/_functools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/_macos_compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/archive_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/cmd.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/_framework_compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/build.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/check.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/clean.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/config.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/register.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/command/upload.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/config.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/core.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/debug.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/dep_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/dir_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/dist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/errors.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/extension.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/file_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/filelist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/log.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/py38compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/py39compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/spawn.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/text_file.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/version.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_entry_points.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_imp.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_importlib.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_itertools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_path.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_reqs.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_collections.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_functools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_meta.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata/_text.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_adapters.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_common.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_itertools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/_legacy.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/abc.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/readers.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/importlib_resources/simple.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/context.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/functools.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/jaraco/text/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/actions.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/common.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/core.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/helpers.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/results.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/testing.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/unicode.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/pyparsing/util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/tomli/_types.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/typing_extensions.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/_vendor/zipp.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/archive_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/build_meta.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/cli-32.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/cli-64.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/cli-arm64.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/cli.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/alias.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/bdist_egg.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/bdist_rpm.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/build.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/build_clib.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/build_ext.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/build_py.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/develop.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/dist_info.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/easy_install.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/editable_wheel.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/egg_info.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/install.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/install_egg_info.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/install_lib.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/install_scripts.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/launcher manifest.xml create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/py36compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/register.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/rotate.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/saveopts.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/sdist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/setopt.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/test.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/upload.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/command/upload_docs.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_apply_pyprojecttoml.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/error_reporting.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/extra_validations.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/formats.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/expand.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/pyprojecttoml.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/config/setupcfg.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/dep_util.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/depends.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/discovery.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/dist.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/errors.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/extension.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/extern/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/glob.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/gui-32.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/gui-64.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/gui-arm64.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/gui.exe create mode 100644 .venv/lib/python3.11/site-packages/setuptools/installer.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/launch.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/logging.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/monkey.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/msvc.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/namespaces.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/package_index.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/py34compat.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/sandbox.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/script (dev).tmpl create mode 100644 .venv/lib/python3.11/site-packages/setuptools/script.tmpl create mode 100644 .venv/lib/python3.11/site-packages/setuptools/unicode_utils.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/version.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/setuptools/windows_support.py create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/six-1.16.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/six.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve-2.8.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/soupsieve-2.8.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/soupsieve-2.8.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/soupsieve-2.8.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/soupsieve-2.8.1.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/__meta__.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/css_match.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/css_parser.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/css_types.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/pretty.py create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/py.typed create mode 100644 .venv/lib/python3.11/site-packages/soupsieve/util.py create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/tomli-2.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/tomli/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/tomli/_parser.py create mode 100644 .venv/lib/python3.11/site-packages/tomli/_re.py create mode 100644 .venv/lib/python3.11/site-packages/tomli/_types.py create mode 100644 .venv/lib/python3.11/site-packages/tomli/py.typed create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.11/site-packages/twine-4.0.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/twine/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/twine/__main__.py create mode 100644 .venv/lib/python3.11/site-packages/twine/auth.py create mode 100644 .venv/lib/python3.11/site-packages/twine/cli.py create mode 100644 .venv/lib/python3.11/site-packages/twine/commands/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/twine/commands/check.py create mode 100644 .venv/lib/python3.11/site-packages/twine/commands/register.py create mode 100644 .venv/lib/python3.11/site-packages/twine/commands/upload.py create mode 100644 .venv/lib/python3.11/site-packages/twine/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/twine/package.py create mode 100644 .venv/lib/python3.11/site-packages/twine/py.typed create mode 100644 .venv/lib/python3.11/site-packages/twine/repository.py create mode 100644 .venv/lib/python3.11/site-packages/twine/settings.py create mode 100644 .venv/lib/python3.11/site-packages/twine/utils.py create mode 100644 .venv/lib/python3.11/site-packages/twine/wheel.py create mode 100644 .venv/lib/python3.11/site-packages/twine/wininst.py create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions-4.15.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions-4.15.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions-4.15.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/typing_extensions.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3-2.6.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/urllib3-2.6.3.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/urllib3-2.6.3.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/urllib3-2.6.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/urllib3-2.6.3.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.11/site-packages/urllib3/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/_base_connection.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/_collections.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/_request_methods.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/_version.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/connection.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/connection.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/fetch.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/request.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/emscripten/response.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/exceptions.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/fields.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/filepost.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/http2/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/http2/connection.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/http2/probe.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/py.typed create mode 100644 .venv/lib/python3.11/site-packages/urllib3/response.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/connection.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/request.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/response.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/retry.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/url.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/util.py create mode 100644 .venv/lib/python3.11/site-packages/urllib3/util/wait.py create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/DESCRIPTION.rst create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/metadata.json create mode 100644 .venv/lib/python3.11/site-packages/webencodings-0.5.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/webencodings/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/webencodings/labels.py create mode 100644 .venv/lib/python3.11/site-packages/webencodings/mklabels.py create mode 100644 .venv/lib/python3.11/site-packages/webencodings/tests.py create mode 100644 .venv/lib/python3.11/site-packages/webencodings/x_user_defined.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/_path.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/_version.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/dsession.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/looponfail.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/newhooks.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/plugin.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/remote.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/report.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/each.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/load.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/loadfile.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/loadgroup.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/loadscope.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/scheduler/worksteal.py create mode 100644 .venv/lib/python3.11/site-packages/xdist/workermanage.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/__init__.py create mode 100755 .venv/lib/python3.11/site-packages/yaml/_yaml.cpython-311-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.11/site-packages/yaml/composer.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/constructor.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/cyaml.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/dumper.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/emitter.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/error.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/events.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/loader.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/nodes.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/parser.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/reader.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/representer.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/resolver.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/scanner.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/serializer.py create mode 100644 .venv/lib/python3.11/site-packages/yaml/tokens.py create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/METADATA create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/RECORD create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.11/site-packages/zipp-3.14.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.11/site-packages/zipp/__init__.py create mode 100644 .venv/lib/python3.11/site-packages/zipp/py310compat.py diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 00000000..b49d77ba --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv/bin/about b/.venv/bin/about new file mode 100755 index 00000000..b995ed59 --- /dev/null +++ b/.venv/bin/about @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from attributecode.cmd import about +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(about()) diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 00000000..2da48916 --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,63 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(.venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(.venv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 00000000..b7f74477 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,26 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(.venv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(.venv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 00000000..68f7f7e8 --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(.venv) " +end diff --git a/.venv/bin/black b/.venv/bin/black new file mode 100755 index 00000000..07582f67 --- /dev/null +++ b/.venv/bin/black @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from black import patched_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(patched_main()) diff --git a/.venv/bin/blackd b/.venv/bin/blackd new file mode 100755 index 00000000..60b837f6 --- /dev/null +++ b/.venv/bin/blackd @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from blackd import patched_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(patched_main()) diff --git a/.venv/bin/docutils b/.venv/bin/docutils new file mode 100755 index 00000000..2af76a2d --- /dev/null +++ b/.venv/bin/docutils @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from docutils.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/isort b/.venv/bin/isort new file mode 100755 index 00000000..3017dcf3 --- /dev/null +++ b/.venv/bin/isort @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/isort-identify-imports b/.venv/bin/isort-identify-imports new file mode 100755 index 00000000..c07854ac --- /dev/null +++ b/.venv/bin/isort-identify-imports @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import identify_imports_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(identify_imports_main()) diff --git a/.venv/bin/keyring b/.venv/bin/keyring new file mode 100755 index 00000000..3aaa1788 --- /dev/null +++ b/.venv/bin/keyring @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from keyring.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/markdown-it b/.venv/bin/markdown-it new file mode 100755 index 00000000..a235416d --- /dev/null +++ b/.venv/bin/markdown-it @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from markdown_it.cli.parse import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer new file mode 100755 index 00000000..588a27db --- /dev/null +++ b/.venv/bin/normalizer @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from charset_normalizer.cli import cli_detect +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli_detect()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 00000000..61f5c64b --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 00000000..61f5c64b --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.11 b/.venv/bin/pip3.11 new file mode 100755 index 00000000..61f5c64b --- /dev/null +++ b/.venv/bin/pip3.11 @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pkginfo b/.venv/bin/pkginfo new file mode 100755 index 00000000..1c0ea202 --- /dev/null +++ b/.venv/bin/pkginfo @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pkginfo.commandline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/py.test b/.venv/bin/py.test new file mode 100755 index 00000000..e1829dc6 --- /dev/null +++ b/.venv/bin/py.test @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/.venv/bin/pycodestyle b/.venv/bin/pycodestyle new file mode 100755 index 00000000..7260fb70 --- /dev/null +++ b/.venv/bin/pycodestyle @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pycodestyle import _main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(_main()) diff --git a/.venv/bin/pygmentize b/.venv/bin/pygmentize new file mode 100755 index 00000000..4fc6e787 --- /dev/null +++ b/.venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pytest b/.venv/bin/pytest new file mode 100755 index 00000000..e1829dc6 --- /dev/null +++ b/.venv/bin/pytest @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(console_main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 00000000..b1161432 --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +/home/diwa-audi/.pyenv/versions/3.11.8/bin/python \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 00000000..d8654aa0 --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/.venv/bin/python3.11 b/.venv/bin/python3.11 new file mode 120000 index 00000000..d8654aa0 --- /dev/null +++ b/.venv/bin/python3.11 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/.venv/bin/rst2html.py b/.venv/bin/rst2html.py new file mode 100755 index 00000000..1eb7cafe --- /dev/null +++ b/.venv/bin/rst2html.py @@ -0,0 +1,23 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2html.py 8927 2022-01-03 23:50:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/.venv/bin/rst2html4.py b/.venv/bin/rst2html4.py new file mode 100755 index 00000000..db4f5cc5 --- /dev/null +++ b/.venv/bin/rst2html4.py @@ -0,0 +1,26 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2html4.py 8927 2022-01-03 23:50:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing (X)HTML. + +The output conforms to XHTML 1.0 transitional +and almost to HTML 4.01 transitional (except for closing empty tags). +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html4', description=description) diff --git a/.venv/bin/rst2html5.py b/.venv/bin/rst2html5.py new file mode 100755 index 00000000..1805a88f --- /dev/null +++ b/.venv/bin/rst2html5.py @@ -0,0 +1,33 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 9021 $ +# Date: $Date: 2022-03-04 16:54:22 +0100 (Fr, 04. Mär 2022) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output is also valid XML. +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = ('Generates HTML5 documents from standalone ' + 'reStructuredText sources.\n' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/.venv/bin/rst2latex.py b/.venv/bin/rst2latex.py new file mode 100755 index 00000000..e467546e --- /dev/null +++ b/.venv/bin/rst2latex.py @@ -0,0 +1,26 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2latex.py 8956 2022-01-20 10:11:44Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/.venv/bin/rst2man.py b/.venv/bin/rst2man.py new file mode 100755 index 00000000..7a8c2b5d --- /dev/null +++ b/.venv/bin/rst2man.py @@ -0,0 +1,27 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/.venv/bin/rst2odt.py b/.venv/bin/rst2odt.py new file mode 100755 index 00000000..bffe171a --- /dev/null +++ b/.venv/bin/rst2odt.py @@ -0,0 +1,28 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2odt.py 8994 2022-01-29 16:28:17Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) diff --git a/.venv/bin/rst2odt_prepstyles.py b/.venv/bin/rst2odt_prepstyles.py new file mode 100755 index 00000000..9adba67b --- /dev/null +++ b/.venv/bin/rst2odt_prepstyles.py @@ -0,0 +1,65 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2odt_prepstyles.py 8932 2022-01-05 14:59:31Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Fix a word-processor-generated styles.odt for odtwriter use: Drop page size +specifications from styles.xml in STYLE_FILE.odt. +""" + +# Author: Michael Schutte + +from lxml import etree +import sys +import zipfile +from tempfile import mkstemp +import shutil +import os + +NAMESPACES = { + "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +} + + +def prepstyle(filename): + + zin = zipfile.ZipFile(filename) + styles = zin.read("styles.xml") + + root = etree.fromstring(styles) + for el in root.xpath("//style:page-layout-properties", + namespaces=NAMESPACES): + for attr in el.attrib: + if attr.startswith("{%s}" % NAMESPACES["fo"]): + del el.attrib[attr] + + tempname = mkstemp() + zout = zipfile.ZipFile(os.fdopen(tempname[0], "w"), "w", + zipfile.ZIP_DEFLATED) + + for item in zin.infolist(): + if item.filename == "styles.xml": + zout.writestr(item, etree.tostring(root)) + else: + zout.writestr(item, zin.read(item.filename)) + + zout.close() + zin.close() + shutil.move(tempname[1], filename) + + +def main(): + args = sys.argv[1:] + if len(args) != 1 or args[0] in ('-h', '--help'): + print(__doc__, file=sys.stderr) + print("Usage: %s STYLE_FILE.odt\n" % sys.argv[0], file=sys.stderr) + sys.exit(1) + filename = args[0] + prepstyle(filename) + + +if __name__ == '__main__': + main() diff --git a/.venv/bin/rst2pseudoxml.py b/.venv/bin/rst2pseudoxml.py new file mode 100755 index 00000000..27f28091 --- /dev/null +++ b/.venv/bin/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2pseudoxml.py 8927 2022-01-03 23:50:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/.venv/bin/rst2s5.py b/.venv/bin/rst2s5.py new file mode 100755 index 00000000..9e051d59 --- /dev/null +++ b/.venv/bin/rst2s5.py @@ -0,0 +1,24 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2s5.py 8927 2022-01-03 23:50:05Z milde $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/.venv/bin/rst2xetex.py b/.venv/bin/rst2xetex.py new file mode 100755 index 00000000..7163a8dc --- /dev/null +++ b/.venv/bin/rst2xetex.py @@ -0,0 +1,27 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2xetex.py 8956 2022-01-20 10:11:44Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/.venv/bin/rst2xml.py b/.venv/bin/rst2xml.py new file mode 100755 index 00000000..8d91f399 --- /dev/null +++ b/.venv/bin/rst2xml.py @@ -0,0 +1,23 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rst2xml.py 8927 2022-01-03 23:50:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/.venv/bin/rstpep2html.py b/.venv/bin/rstpep2html.py new file mode 100755 index 00000000..5c25e583 --- /dev/null +++ b/.venv/bin/rstpep2html.py @@ -0,0 +1,25 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python + +# $Id: rstpep2html.py 8927 2022-01-03 23:50:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/.venv/bin/twine b/.venv/bin/twine new file mode 100755 index 00000000..af428297 --- /dev/null +++ b/.venv/bin/twine @@ -0,0 +1,8 @@ +#!/home/diwa-audi/Documents/codes/openSource/contri_2/commoncode/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from twine.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/lib/python3.11/site-packages/6b397dd64e00b5aff23d__mypyc.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/6b397dd64e00b5aff23d__mypyc.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..79e5015e749f470e0fc36d2780b0884ffa2ff31b GIT binary patch literal 3993856 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

A4yybWc?$krM12l~fyXvGTJC$! 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/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�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
sN){YQMl~ybIN<^cs#zkJ_UaNo$|JkG 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ù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%0AFGs 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 F(?YK|V00dJS{7t(>+k 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 A;&ekntcKwJyW_xzHyn*jLyQv;{ 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 D zt@$gCFYz&2jAAyMrm(yIfNUJ#`j);R9^>=)0h`#LD-jn{Pb0JZDWL`O+gW#q*SO_R zO5@r=ZsP?u9c}V8Y!P%qrD51l+V{`m$|u?8X?b$O^pfiV^QtY|$lQx3?(IL&MM2$A zxDkEltIt7VSJGxTvhvzU<#jcBKDKb|3;g2f`v+(lN8hJ${YS6Y6kLhDK2*S12RHyf zh>9Zx9Z_VPhowNdV0bcf4A@aIM!nwWw|gx4)6Dg$HxR(Q)KfhOKX*ZZK4Zyo>}=(%E1Q zTW^y`1zXr!nNodA9%$HFnHs11+!*Pk&@wi-V27el7^KVkhlhdQEzLR+=aCGLsSorP zrv3nNQzhzwlekc#jbEa#*|1t8By$WM=@?*1*(8g=GBP7r}+ z9xkPclN~LG(0i)Hts)nDyKRdQAhlfkud)!nn|<{UJJv^4P!D?ElpANqmg9$SIp^_5 zWR{m!!YB>9Zl(ib;;x&sw|O%5*JGqVb+8ChN-pF4NVRG3&4v4XHMae=W1@}r-1mQG zplAKwRxx`F#>ekaOZO_SYU#rA(VkRNf{1blNtTrgAsOLcabV*oej}-JhGEiN1fw62`Mnd!xihk z=LUJLorfU=y3uwv44e`O-ysvc;KnM)h!?P;qBp@=!Xi{zKxad>Jl z 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

z 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst new file mode 100644 index 00000000..c37cae49 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA new file mode 100644 index 00000000..f54bb5ca --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.2 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} +

+ {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD new file mode 100644 index 00000000..b145ed26 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/RECORD @@ -0,0 +1,59 @@ +Jinja2-3.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.1.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.1.2.dist-info/METADATA,sha256=PZ6v2SIidMNixR7MRUX9f7ZWsPwtXanknqiZUmRbh4U,3539 +Jinja2-3.1.2.dist-info/RECORD,, +Jinja2-3.1.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Jinja2-3.1.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Jinja2-3.1.2.dist-info/entry_points.txt,sha256=zRd62fbqIyfUpsRtU7EVIFyiu1tPwfgO7EvPErnxgTE,59 +Jinja2-3.1.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=8vGduD8ytwgD6GDSqpYc2m3aU-T7PKOAddvVXgGr_Fs,1927 +jinja2/__pycache__/__init__.cpython-311.pyc,, +jinja2/__pycache__/_identifier.cpython-311.pyc,, +jinja2/__pycache__/async_utils.cpython-311.pyc,, +jinja2/__pycache__/bccache.cpython-311.pyc,, +jinja2/__pycache__/compiler.cpython-311.pyc,, +jinja2/__pycache__/constants.cpython-311.pyc,, +jinja2/__pycache__/debug.cpython-311.pyc,, +jinja2/__pycache__/defaults.cpython-311.pyc,, +jinja2/__pycache__/environment.cpython-311.pyc,, +jinja2/__pycache__/exceptions.cpython-311.pyc,, +jinja2/__pycache__/ext.cpython-311.pyc,, +jinja2/__pycache__/filters.cpython-311.pyc,, +jinja2/__pycache__/idtracking.cpython-311.pyc,, +jinja2/__pycache__/lexer.cpython-311.pyc,, +jinja2/__pycache__/loaders.cpython-311.pyc,, +jinja2/__pycache__/meta.cpython-311.pyc,, +jinja2/__pycache__/nativetypes.cpython-311.pyc,, +jinja2/__pycache__/nodes.cpython-311.pyc,, +jinja2/__pycache__/optimizer.cpython-311.pyc,, +jinja2/__pycache__/parser.cpython-311.pyc,, +jinja2/__pycache__/runtime.cpython-311.pyc,, +jinja2/__pycache__/sandbox.cpython-311.pyc,, +jinja2/__pycache__/tests.cpython-311.pyc,, +jinja2/__pycache__/utils.cpython-311.pyc,, +jinja2/__pycache__/visitor.cpython-311.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=dHlbTeaxFPtAOQEYOGYh_PHcDT0rsDaUJAFDl_0XtTg,2472 +jinja2/bccache.py,sha256=mhz5xtLxCcHRAa56azOhphIAe19u1we0ojifNMClDio,14061 +jinja2/compiler.py,sha256=Gs-N8ThJ7OWK4-reKoO8Wh1ZXz95MVphBKNVf75qBr8,72172 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=6uHIcc7ZblqOMdx_uYNKqRnnwAF0_nzbyeMP9FFtuh4,61349 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=ivr3P7LKbddiXDVez20EflcO3q2aHQwz9P_PgWGHVqE,31502 +jinja2/filters.py,sha256=9js1V-h2RlyW90IhLiBGLM2U-k6SCy2F4BUUMgB3K9Q,53509 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=DW2nX9zk-6MWp65YR2bqqj0xqCvLtD-u9NWT8AnFRxQ,29726 +jinja2/loaders.py,sha256=BfptfvTVpClUd-leMkHczdyPNYFzp_n7PKOJ98iyHOg,23207 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=DXgORDPRmVWgy034H0xL8eF7qYoK3DrMxs-935d0Fzk,4226 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=nHd-DFHbiygvfaPtm9rcQXJChZG7DPsWfiEsqfwKerY,39595 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=5CmD5BjbEJxSiDNTFBeKCaq8qU4aYD2v6q2EluyExms,33476 +jinja2/sandbox.py,sha256=Y0xZeXQnH6EX5VjaV2YixESxoepnRbW_3UeQosaBU3M,14584 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=u9jXESxGn8ATZNVolwmkjUVu4SA-tLgV0W7PcSfPfdQ,23965 +jinja2/visitor.py,sha256=MH14C6yq24G_KVtWzjwaI7Wg14PCJIYlWW1kpkxYak0,3568 diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL new file mode 100644 index 00000000..becc9a66 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt new file mode 100644 index 00000000..7b9666c8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract[i18n] diff --git a/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt new file mode 100644 index 00000000..7f7afbf3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Jinja2-3.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst new file mode 100644 index 00000000..9d227a0c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA new file mode 100644 index 00000000..4a349997 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.2 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD new file mode 100644 index 00000000..d8ef0c00 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/RECORD @@ -0,0 +1,15 @@ +MarkupSafe-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.2.dist-info/METADATA,sha256=jPw4iOiZg6adxZ5bdvjZapeNmPMINMGG2q2v2rI4SqA,3222 +MarkupSafe-2.1.2.dist-info/RECORD,, +MarkupSafe-2.1.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +MarkupSafe-2.1.2.dist-info/WHEEL,sha256=zcODeksSPXfEVqloqbuxG5DqZN6qJmVdi0EN9bjsiGQ,152 +MarkupSafe-2.1.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=LtjnhQ6AHmAgHl37cev2oQBXjr4xOF-QhdXgsCAL3-0,9306 +markupsafe/__pycache__/__init__.cpython-311.pyc,, +markupsafe/__pycache__/_native.cpython-311.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-311-x86_64-linux-gnu.so,sha256=oxS10ynvGJxxD2aGbEyu_VASpUD-XwDsjQOlaw6cSzs,53656 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL new file mode 100644 index 00000000..9a9cfe17 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: false +Tag: cp311-cp311-manylinux_2_17_x86_64 +Tag: cp311-cp311-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt new file mode 100644 index 00000000..75bf7292 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/AUTHORS b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/AUTHORS new file mode 100644 index 00000000..18a32c68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/AUTHORS @@ -0,0 +1,264 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Sam Aaron -- Ioke lexer +* Jean Abou Samra -- LilyPond lexer +* João Abecasis -- JSLT lexer +* Ali Afshar -- image formatter +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers +* Muthiah Annamalai -- Ezhil lexer +* Kumar Appaiah -- Debian control lexer +* Andreas Amann -- AppleScript lexer +* Timothy Armstrong -- Dart lexer fixes +* Jeffrey Arnold -- R/S, Rd, BUGS, Jags, and Stan lexers +* Eiríkr Åsheim -- Uxntal lexer +* Jeremy Ashkenas -- CoffeeScript lexer +* José Joaquín Atria -- Praat lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Lucas Bajolet -- Nit lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Thomas Baruchel -- APL lexer +* Tim Baumann -- (Literate) Agda lexer +* Paul Baumgart, 280 North, Inc. -- Objective-J lexer +* Michael Bayer -- Myghty lexers +* Thomas Beale -- Archetype lexers +* John Benediktsson -- Factor lexer +* Trevor Bergeron -- mIRC formatter +* Vincent Bernat -- LessCSS lexer +* Christopher Bertels -- Fancy lexer +* Sébastien Bigaret -- QVT Operational lexer +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Stéphane Blondon -- Procfile, SGF and Sieve lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Martijn Braam -- Kernel log lexer, BARE lexer +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter +* chebee7i -- Python traceback lexer improvements +* Hiram Chirino -- Scaml and Jade lexers +* Mauricio Caceres -- SAS and Stata lexers. +* Ian Cooper -- VGL lexer +* David Corbett -- Inform, Jasmin, JSGF, Snowball, and TADS 3 lexers +* Leaf Corcoran -- MoonScript lexer +* Christopher Creutzig -- MuPAD lexer +* Daniël W. Crompton -- Pike lexer +* Pete Curry -- bugfixes +* Bryan Davis -- EBNF lexer +* Bruno Deferrari -- Shen lexer +* Walter Dörwald -- UL4 lexer +* Luke Drummond -- Meson lexer +* Giedrius Dubinskas -- HTML formatter improvements +* Owen Durni -- Haxe lexer +* Alexander Dutton, Oxford University Computing Services -- SPARQL lexer +* James Edwards -- Terraform lexer +* Nick Efford -- Python 3 lexer +* Sven Efftinge -- Xtend lexer +* Artem Egorkine -- terminal256 formatter +* Matthew Fernandez -- CAmkES lexer +* Paweł Fertyk -- GDScript lexer, HTML formatter improvements +* Michael Ficarra -- CPSA lexer +* James H. Fisher -- PostScript lexer +* William S. Fulton -- SWIG lexer +* Carlos Galdino -- Elixir and Elixir Console lexers +* Michael Galloy -- IDL lexer +* Naveen Garg -- Autohotkey lexer +* Simon Garnotel -- FreeFem++ lexer +* Laurent Gautier -- R/S lexer +* Alex Gaynor -- PyPy log lexer +* Richard Gerkin -- Igor Pro lexer +* Alain Gilbert -- TypeScript lexer +* Alex Gilding -- BlitzBasic lexer +* GitHub, Inc -- DASM16, Augeas, TOML, and Slash lexers +* Bertrand Goetzmann -- Groovy lexer +* Krzysiek Goj -- Scala lexer +* Rostyslav Golda -- FloScript lexer +* Andrey Golovizin -- BibTeX lexers +* Matt Good -- Genshi, Cheetah lexers +* Michał Górny -- vim modeline support +* Alex Gosse -- TrafficScript lexer +* Patrick Gotthardt -- PHP namespaces support +* Hubert Gruniaux -- C and C++ lexer improvements +* Olivier Guibe -- Asymptote lexer +* Phil Hagelberg -- Fennel lexer +* Florian Hahn -- Boogie lexer +* Martin Harriman -- SNOBOL lexer +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Dan Michael Heggø -- Turtle lexer +* Aslak Hellesøy -- Gherkin lexer +* Greg Hendershott -- Racket lexer +* Justin Hendrick -- ParaSail lexer +* Jordi Gutiérrez Hermoso -- Octave lexer +* David Hess, Fish Software, Inc. -- Objective-J lexer +* Ken Hilton -- Typographic Number Theory and Arrow lexers +* Varun Hiremath -- Debian control lexer +* Rob Hoelz -- Perl 6 lexer +* Doug Hogan -- Mscgen lexer +* Ben Hollis -- Mason lexer +* Max Horn -- GAP lexer +* Fred Hornsey -- OMG IDL Lexer +* Alastair Houghton -- Lexer inheritance facility +* Tim Howard -- BlitzMax lexer +* Dustin Howett -- Logos lexer +* Ivan Inozemtsev -- Fantom lexer +* Hiroaki Itoh -- Shell console rewrite, Lexers for PowerShell session, + MSDOS session, BC, WDiff +* Brian R. Jackson -- Tea lexer +* Christian Jann -- ShellSession lexer +* Dennis Kaarsemaker -- sources.list lexer +* Dmitri Kabak -- Inferno Limbo lexer +* Igor Kalnitsky -- vhdl lexer +* Colin Kennedy - USD lexer +* Alexander Kit -- MaskJS lexer +* Pekka Klärck -- Robot Framework lexer +* Gerwin Klein -- Isabelle lexer +* Eric Knibbe -- Lasso lexer +* Stepan Koltsov -- Clay lexer +* Oliver Kopp - Friendly grayscale style +* Adam Koprowski -- Opa lexer +* Benjamin Kowarsch -- Modula-2 lexer +* Domen Kožar -- Nix lexer +* Oleh Krekel -- Emacs Lisp lexer +* Alexander Kriegisch -- Kconfig and AspectJ lexers +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Jon Larimer, Google Inc. -- Smali lexer +* Olov Lassus -- Dart lexer +* Matt Layman -- TAP lexer +* Kristian Lyngstøl -- Varnish lexers +* Sylvestre Ledru -- Scilab lexer +* Chee Sing Lee -- Flatline lexer +* Mark Lee -- Vala lexer +* Pete Lomax -- Phix lexer +* Valentin Lorentz -- C++ lexer improvements +* Ben Mabey -- Gherkin lexer +* Angus MacArthur -- QML lexer +* Louis Mandel -- X10 lexer +* Louis Marchand -- Eiffel lexer +* Simone Margaritelli -- Hybris lexer +* Tim Martin - World of Warcraft TOC lexer +* Kirk McDonald -- D lexer +* Gordon McGregor -- SystemVerilog lexer +* Stephen McKamey -- Duel/JBST lexer +* Brian McKenna -- F# lexer +* Charles McLaughlin -- Puppet lexer +* Kurt McKee -- Tera Term macro lexer, PostgreSQL updates, MySQL overhaul, JSON lexer +* Joe Eli McIlvain -- Savi lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Cat Miller -- Pig lexer +* Paul Miller -- LiveScript lexer +* Hong Minhee -- HTTP lexer +* Michael Mior -- Awk lexer +* Bruce Mitchener -- Dylan lexer rewrite +* Reuben Morais -- SourcePawn lexer +* Jon Morton -- Rust lexer +* Paulo Moura -- Logtalk lexer +* Mher Movsisyan -- DTD lexer +* Dejan Muhamedagic -- Crmsh lexer +* Ana Nelson -- Ragel, ANTLR, R console lexers +* Kurt Neufeld -- Markdown lexer +* Nam T. Nguyen -- Monokai style +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Mike Nolta -- Julia lexer +* Avery Nortonsmith -- Pointless lexer +* Jonas Obrist -- BBCode lexer +* Edward O'Callaghan -- Cryptol lexer +* David Oliva -- Rebol lexer +* Pat Pannuto -- nesC lexer +* Jon Parise -- Protocol buffers and Thrift lexers +* Benjamin Peterson -- Test suite refactoring +* Ronny Pfannschmidt -- BBCode lexer +* Dominik Picheta -- Nimrod lexer +* Andrew Pinkham -- RTF Formatter Refactoring +* Clément Prévost -- UrbiScript lexer +* Tanner Prynn -- cmdline -x option and loading lexers from files +* Oleh Prypin -- Crystal lexer (based on Ruby lexer) +* Nick Psaris -- K and Q lexers +* Xidorn Quan -- Web IDL lexer +* Elias Rabel -- Fortran fixed form lexer +* raichoo -- Idris lexer +* Daniel Ramirez -- GDScript lexer +* Kashif Rasul -- CUDA lexer +* Nathan Reed -- HLSL lexer +* Justin Reidy -- MXML lexer +* Norman Richards -- JSON lexer +* Corey Richardson -- Rust lexer updates +* Fabrizio Riguzzi -- cplint leder +* Lubomir Rintel -- GoodData MAQL and CL lexers +* Andre Roberge -- Tango style +* Georg Rollinger -- HSAIL lexer +* Michiel Roos -- TypoScript lexer +* Konrad Rudolph -- LaTeX formatter enhancements +* Mario Ruggier -- Evoque lexers +* Miikka Salminen -- Lovelace style, Hexdump lexer, lexer enhancements +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Joe Schafer -- Ada lexer +* Max Schillinger -- TiddlyWiki5 lexer +* Ken Schutte -- Matlab lexers +* René Schwaiger -- Rainbow Dash style +* Sebastian Schweizer -- Whiley lexer +* Tassilo Schweyer -- Io, MOOCode lexers +* Pablo Seminario -- PromQL lexer +* Ted Shaw -- AutoIt lexer +* Joerg Sieker -- ABAP lexer +* Robert Simmons -- Standard ML lexer +* Kirill Simonov -- YAML lexer +* Corbin Simpson -- Monte lexer +* Ville Skyttä -- ASCII armored lexer +* Alexander Smishlajev -- Visual FoxPro lexer +* Steve Spigarelli -- XQuery lexer +* Jerome St-Louis -- eC lexer +* Camil Staps -- Clean and NuSMV lexers; Solarized style +* James Strachan -- Kotlin lexer +* Tom Stuart -- Treetop lexer +* Colin Sullivan -- SuperCollider lexer +* Ben Swift -- Extempore lexer +* tatt61880 -- Kuin lexer +* Edoardo Tenani -- Arduino lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Brian Tiffin -- OpenCOBOL lexer +* Bob Tolbert -- Hy lexer +* Doug Torrance -- Macaulay2 lexer +* Matthias Trute -- Forth lexer +* Tuoa Spi T4 -- Bdd lexer +* Erick Tryzelaar -- Felix lexer +* Alexander Udalov -- Kotlin lexer improvements +* Thomas Van Doren -- Chapel lexer +* Daniele Varrazzo -- PostgreSQL lexers +* Abe Voelker -- OpenEdge ABL lexer +* Pepijn de Vos -- HTML formatter CTags support +* Matthias Vallentin -- Bro lexer +* Benoît Vinot -- AMPL lexer +* Linh Vu Hong -- RSL lexer +* Immanuel Washington -- Smithy lexer +* Nathan Weizenbaum -- Haml and Sass lexers +* Nathan Whetsell -- Csound lexers +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer +* Whitney Young -- ObjectiveC lexer +* Diego Zamboni -- CFengine3 lexer +* Enrique Zamudio -- Ceylon lexer +* Alex Zimin -- Nemerle lexer +* Rob Zimmerman -- Kal lexer +* Vincent Zurczak -- Roboconf lexer +* Hubert Gruniaux -- C and C++ lexer improvements +* Thomas Symalla -- AMDGPU Lexer +* 15b3 -- Image Formatter improvements +* Fabian Neumann -- CDDL lexer +* Thomas Duboucher -- CDDL lexer +* Philipp Imhof -- Pango Markup formatter +* Thomas Voss -- Sed lexer +* Martin Fischer -- WCAG contrast testing +* Marc Auberer -- Spice lexer + +Many thanks for all contributions! diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/LICENSE new file mode 100644 index 00000000..446a1a80 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2022 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/METADATA new file mode 100644 index 00000000..0ba0902a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/METADATA @@ -0,0 +1,38 @@ +Metadata-Version: 2.1 +Name: Pygments +Version: 2.14.0 +Summary: Pygments is a syntax highlighting package written in Python. +Home-page: https://pygments.org/ +Author: Georg Brandl +Author-email: georg@python.org +License: BSD-2-Clause +Project-URL: Documentation, https://pygments.org/docs/ +Project-URL: Source, https://github.com/pygments/pygments +Project-URL: Bug Tracker, https://github.com/pygments/pygments/issues +Project-URL: Changelog, https://github.com/pygments/pygments/blob/master/CHANGES +Keywords: syntax highlighting +Platform: any +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Utilities +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS +Provides-Extra: plugins +Requires-Dist: importlib-metadata ; (python_version < "3.8") and extra == 'plugins' + diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/RECORD new file mode 100644 index 00000000..92145872 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/RECORD @@ -0,0 +1,608 @@ +../../../bin/pygmentize,sha256=X8XJo2AvwrXPwXq7UHJtJKhAfcd21krqX3S4VkTNaM0,278 +Pygments-2.14.0.dist-info/AUTHORS,sha256=OzlKwZii64dQrA-dyL7ayg_ogm5_OXUi_uwMmFwC3Xo,9737 +Pygments-2.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Pygments-2.14.0.dist-info/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331 +Pygments-2.14.0.dist-info/METADATA,sha256=SnXak0f9bFv55eBI1WcwoHxo06g8XQC13OzWyskwwxk,1606 +Pygments-2.14.0.dist-info/RECORD,, +Pygments-2.14.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Pygments-2.14.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +Pygments-2.14.0.dist-info/entry_points.txt,sha256=uUXw-XhMKBEX4pWcCtpuTTnPhL3h7OEE2jWi51VQsa8,53 +Pygments-2.14.0.dist-info/top_level.txt,sha256=RjKKqrVIStoebLHdbs0yZ2Lk4rS7cxGguXsLCYvZ2Ak,9 +pygments/__init__.py,sha256=G6KeoinsUh_ixOUfvUm9YQbF853QdZ4xxinie2AxBi0,2975 +pygments/__main__.py,sha256=H4FKgvsbzW61IXZbQ7n6JYJg674Z5MrYCWl4SvdXl4k,348 +pygments/__pycache__/__init__.cpython-311.pyc,, +pygments/__pycache__/__main__.cpython-311.pyc,, +pygments/__pycache__/cmdline.cpython-311.pyc,, +pygments/__pycache__/console.cpython-311.pyc,, +pygments/__pycache__/filter.cpython-311.pyc,, +pygments/__pycache__/formatter.cpython-311.pyc,, +pygments/__pycache__/lexer.cpython-311.pyc,, +pygments/__pycache__/modeline.cpython-311.pyc,, +pygments/__pycache__/plugin.cpython-311.pyc,, +pygments/__pycache__/regexopt.cpython-311.pyc,, +pygments/__pycache__/scanner.cpython-311.pyc,, +pygments/__pycache__/sphinxext.cpython-311.pyc,, +pygments/__pycache__/style.cpython-311.pyc,, +pygments/__pycache__/token.cpython-311.pyc,, +pygments/__pycache__/unistring.cpython-311.pyc,, +pygments/__pycache__/util.cpython-311.pyc,, +pygments/cmdline.py,sha256=fHBl85_v0FRqjw-Krp9-QcjIjwoHsq7OcVhHAkUJDvA,23530 +pygments/console.py,sha256=hQfqCFuOlGk7DW2lPQYepsw-wkOH1iNt9ylNA1eRymM,1697 +pygments/filter.py,sha256=NglMmMPTRRv-zuRSE_QbWid7JXd2J4AvwjCW2yWALXU,1938 +pygments/filters/__init__.py,sha256=Te1zPTE-avGuVnGrBVrOuLP89Q_g8trWgJvsPR5A00A,40338 +pygments/filters/__pycache__/__init__.cpython-311.pyc,, +pygments/formatter.py,sha256=5mKfYy6YY9mvQ-t4vqMNuNqRBIeEIJCSdRixbniZ68E,2893 +pygments/formatters/__init__.py,sha256=DjQIdYN6tqUG-yaGE88vdRO84MORotwpDonds73WNtY,4764 +pygments/formatters/__pycache__/__init__.cpython-311.pyc,, +pygments/formatters/__pycache__/_mapping.cpython-311.pyc,, +pygments/formatters/__pycache__/bbcode.cpython-311.pyc,, +pygments/formatters/__pycache__/groff.cpython-311.pyc,, +pygments/formatters/__pycache__/html.cpython-311.pyc,, +pygments/formatters/__pycache__/img.cpython-311.pyc,, +pygments/formatters/__pycache__/irc.cpython-311.pyc,, +pygments/formatters/__pycache__/latex.cpython-311.pyc,, +pygments/formatters/__pycache__/other.cpython-311.pyc,, +pygments/formatters/__pycache__/pangomarkup.cpython-311.pyc,, +pygments/formatters/__pycache__/rtf.cpython-311.pyc,, +pygments/formatters/__pycache__/svg.cpython-311.pyc,, +pygments/formatters/__pycache__/terminal.cpython-311.pyc,, +pygments/formatters/__pycache__/terminal256.cpython-311.pyc,, +pygments/formatters/_mapping.py,sha256=fCZgvsM6UEuZUG7J6lr47eVss5owKd_JyaNbDfxeqmQ,4104 +pygments/formatters/bbcode.py,sha256=aHDvFf02NTvo1oyFh7d7Nc-diEuJENhYHfgO5ni8y-c,3290 +pygments/formatters/groff.py,sha256=YwId80AP8EkfwEJHmKhHC7cGSugSLROhALR6si4TaOI,5062 +pygments/formatters/html.py,sha256=hXjnogf8HKeUOU-Xfklt52ult8dWKbSIvswmrfQGqc8,35565 +pygments/formatters/img.py,sha256=ErNknc6pWtb20k3OmOmbx9Yk2ltn3gJfIf3kztIfnAg,21914 +pygments/formatters/irc.py,sha256=BvaE_RpG1sWZY-HsV3rSOB6yfHiursKer-z4FoMAplE,4945 +pygments/formatters/latex.py,sha256=hu2im0X4ORQ1ctOwWbHsDqyJwBMD7QQVps0IkgIz6yk,19303 +pygments/formatters/other.py,sha256=dLf0bcqCxQmSk0NdEUdQMjDBw6l-Uqr3ioLbM-xE8kQ,5025 +pygments/formatters/pangomarkup.py,sha256=zTQdtK1097fcGfOM4NOItdp966y448deui0pxveauHQ,2200 +pygments/formatters/rtf.py,sha256=VrqLrq7dGZpxCkydzWbE0PWz7CS3h8rcrNXgNotQ178,4990 +pygments/formatters/svg.py,sha256=hrRO4p-XoOGxf9_6y2SVysO-o1h_AdPMsVwaNbKf1Ww,7299 +pygments/formatters/terminal.py,sha256=ExPmcdOucmPNXeXGxHZe1gs1ihoDtiPVn8eswy0CTiM,4626 +pygments/formatters/terminal256.py,sha256=YWmNHtnycxCPe0IeIxSrUziquIzejubuZMkG9a4Mubw,11717 +pygments/lexer.py,sha256=pG-6VE6DB8oXjxmG9SgDBTsIpf4afTDG6USMFBlRhRY,31987 +pygments/lexers/__init__.py,sha256=na2cMfV3HpzF0xweVrzn3OT-AusS1xJv_SnOkcBBb6s,11116 +pygments/lexers/__pycache__/__init__.cpython-311.pyc,, +pygments/lexers/__pycache__/_ada_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_asy_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_cl_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_cocoa_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_csound_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_css_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_julia_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_lasso_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_lilypond_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_lua_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_mapping.cpython-311.pyc,, +pygments/lexers/__pycache__/_mql_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_mysql_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_openedge_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_php_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_postgres_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_qlik_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_scheme_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_scilab_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_sourcemod_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_stan_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_stata_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_tsql_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_usd_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_vbscript_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/_vim_builtins.cpython-311.pyc,, +pygments/lexers/__pycache__/actionscript.cpython-311.pyc,, +pygments/lexers/__pycache__/ada.cpython-311.pyc,, +pygments/lexers/__pycache__/agile.cpython-311.pyc,, +pygments/lexers/__pycache__/algebra.cpython-311.pyc,, +pygments/lexers/__pycache__/ambient.cpython-311.pyc,, +pygments/lexers/__pycache__/amdgpu.cpython-311.pyc,, +pygments/lexers/__pycache__/ampl.cpython-311.pyc,, +pygments/lexers/__pycache__/apdlexer.cpython-311.pyc,, +pygments/lexers/__pycache__/apl.cpython-311.pyc,, +pygments/lexers/__pycache__/archetype.cpython-311.pyc,, +pygments/lexers/__pycache__/arrow.cpython-311.pyc,, +pygments/lexers/__pycache__/arturo.cpython-311.pyc,, +pygments/lexers/__pycache__/asc.cpython-311.pyc,, +pygments/lexers/__pycache__/asm.cpython-311.pyc,, +pygments/lexers/__pycache__/automation.cpython-311.pyc,, +pygments/lexers/__pycache__/bare.cpython-311.pyc,, +pygments/lexers/__pycache__/basic.cpython-311.pyc,, +pygments/lexers/__pycache__/bdd.cpython-311.pyc,, +pygments/lexers/__pycache__/berry.cpython-311.pyc,, +pygments/lexers/__pycache__/bibtex.cpython-311.pyc,, +pygments/lexers/__pycache__/boa.cpython-311.pyc,, +pygments/lexers/__pycache__/business.cpython-311.pyc,, +pygments/lexers/__pycache__/c_cpp.cpython-311.pyc,, +pygments/lexers/__pycache__/c_like.cpython-311.pyc,, +pygments/lexers/__pycache__/capnproto.cpython-311.pyc,, +pygments/lexers/__pycache__/cddl.cpython-311.pyc,, +pygments/lexers/__pycache__/chapel.cpython-311.pyc,, +pygments/lexers/__pycache__/clean.cpython-311.pyc,, +pygments/lexers/__pycache__/comal.cpython-311.pyc,, +pygments/lexers/__pycache__/compiled.cpython-311.pyc,, +pygments/lexers/__pycache__/configs.cpython-311.pyc,, +pygments/lexers/__pycache__/console.cpython-311.pyc,, +pygments/lexers/__pycache__/cplint.cpython-311.pyc,, +pygments/lexers/__pycache__/crystal.cpython-311.pyc,, +pygments/lexers/__pycache__/csound.cpython-311.pyc,, +pygments/lexers/__pycache__/css.cpython-311.pyc,, +pygments/lexers/__pycache__/d.cpython-311.pyc,, +pygments/lexers/__pycache__/dalvik.cpython-311.pyc,, +pygments/lexers/__pycache__/data.cpython-311.pyc,, +pygments/lexers/__pycache__/devicetree.cpython-311.pyc,, +pygments/lexers/__pycache__/diff.cpython-311.pyc,, +pygments/lexers/__pycache__/dotnet.cpython-311.pyc,, +pygments/lexers/__pycache__/dsls.cpython-311.pyc,, +pygments/lexers/__pycache__/dylan.cpython-311.pyc,, +pygments/lexers/__pycache__/ecl.cpython-311.pyc,, +pygments/lexers/__pycache__/eiffel.cpython-311.pyc,, +pygments/lexers/__pycache__/elm.cpython-311.pyc,, +pygments/lexers/__pycache__/elpi.cpython-311.pyc,, +pygments/lexers/__pycache__/email.cpython-311.pyc,, +pygments/lexers/__pycache__/erlang.cpython-311.pyc,, +pygments/lexers/__pycache__/esoteric.cpython-311.pyc,, +pygments/lexers/__pycache__/ezhil.cpython-311.pyc,, +pygments/lexers/__pycache__/factor.cpython-311.pyc,, +pygments/lexers/__pycache__/fantom.cpython-311.pyc,, +pygments/lexers/__pycache__/felix.cpython-311.pyc,, +pygments/lexers/__pycache__/fift.cpython-311.pyc,, +pygments/lexers/__pycache__/floscript.cpython-311.pyc,, +pygments/lexers/__pycache__/forth.cpython-311.pyc,, +pygments/lexers/__pycache__/fortran.cpython-311.pyc,, +pygments/lexers/__pycache__/foxpro.cpython-311.pyc,, +pygments/lexers/__pycache__/freefem.cpython-311.pyc,, +pygments/lexers/__pycache__/func.cpython-311.pyc,, +pygments/lexers/__pycache__/functional.cpython-311.pyc,, +pygments/lexers/__pycache__/futhark.cpython-311.pyc,, +pygments/lexers/__pycache__/gcodelexer.cpython-311.pyc,, +pygments/lexers/__pycache__/gdscript.cpython-311.pyc,, +pygments/lexers/__pycache__/go.cpython-311.pyc,, +pygments/lexers/__pycache__/grammar_notation.cpython-311.pyc,, +pygments/lexers/__pycache__/graph.cpython-311.pyc,, +pygments/lexers/__pycache__/graphics.cpython-311.pyc,, +pygments/lexers/__pycache__/graphviz.cpython-311.pyc,, +pygments/lexers/__pycache__/gsql.cpython-311.pyc,, +pygments/lexers/__pycache__/haskell.cpython-311.pyc,, +pygments/lexers/__pycache__/haxe.cpython-311.pyc,, +pygments/lexers/__pycache__/hdl.cpython-311.pyc,, +pygments/lexers/__pycache__/hexdump.cpython-311.pyc,, +pygments/lexers/__pycache__/html.cpython-311.pyc,, +pygments/lexers/__pycache__/idl.cpython-311.pyc,, +pygments/lexers/__pycache__/igor.cpython-311.pyc,, +pygments/lexers/__pycache__/inferno.cpython-311.pyc,, +pygments/lexers/__pycache__/installers.cpython-311.pyc,, +pygments/lexers/__pycache__/int_fiction.cpython-311.pyc,, +pygments/lexers/__pycache__/iolang.cpython-311.pyc,, +pygments/lexers/__pycache__/j.cpython-311.pyc,, +pygments/lexers/__pycache__/javascript.cpython-311.pyc,, +pygments/lexers/__pycache__/jmespath.cpython-311.pyc,, +pygments/lexers/__pycache__/jslt.cpython-311.pyc,, +pygments/lexers/__pycache__/jsonnet.cpython-311.pyc,, +pygments/lexers/__pycache__/julia.cpython-311.pyc,, +pygments/lexers/__pycache__/jvm.cpython-311.pyc,, +pygments/lexers/__pycache__/kuin.cpython-311.pyc,, +pygments/lexers/__pycache__/lilypond.cpython-311.pyc,, +pygments/lexers/__pycache__/lisp.cpython-311.pyc,, +pygments/lexers/__pycache__/macaulay2.cpython-311.pyc,, +pygments/lexers/__pycache__/make.cpython-311.pyc,, +pygments/lexers/__pycache__/markup.cpython-311.pyc,, +pygments/lexers/__pycache__/math.cpython-311.pyc,, +pygments/lexers/__pycache__/matlab.cpython-311.pyc,, +pygments/lexers/__pycache__/maxima.cpython-311.pyc,, +pygments/lexers/__pycache__/meson.cpython-311.pyc,, +pygments/lexers/__pycache__/mime.cpython-311.pyc,, +pygments/lexers/__pycache__/minecraft.cpython-311.pyc,, +pygments/lexers/__pycache__/mips.cpython-311.pyc,, +pygments/lexers/__pycache__/ml.cpython-311.pyc,, +pygments/lexers/__pycache__/modeling.cpython-311.pyc,, +pygments/lexers/__pycache__/modula2.cpython-311.pyc,, +pygments/lexers/__pycache__/monte.cpython-311.pyc,, +pygments/lexers/__pycache__/mosel.cpython-311.pyc,, +pygments/lexers/__pycache__/ncl.cpython-311.pyc,, +pygments/lexers/__pycache__/nimrod.cpython-311.pyc,, +pygments/lexers/__pycache__/nit.cpython-311.pyc,, +pygments/lexers/__pycache__/nix.cpython-311.pyc,, +pygments/lexers/__pycache__/oberon.cpython-311.pyc,, +pygments/lexers/__pycache__/objective.cpython-311.pyc,, +pygments/lexers/__pycache__/ooc.cpython-311.pyc,, +pygments/lexers/__pycache__/other.cpython-311.pyc,, +pygments/lexers/__pycache__/parasail.cpython-311.pyc,, +pygments/lexers/__pycache__/parsers.cpython-311.pyc,, +pygments/lexers/__pycache__/pascal.cpython-311.pyc,, +pygments/lexers/__pycache__/pawn.cpython-311.pyc,, +pygments/lexers/__pycache__/perl.cpython-311.pyc,, +pygments/lexers/__pycache__/phix.cpython-311.pyc,, +pygments/lexers/__pycache__/php.cpython-311.pyc,, +pygments/lexers/__pycache__/pointless.cpython-311.pyc,, +pygments/lexers/__pycache__/pony.cpython-311.pyc,, +pygments/lexers/__pycache__/praat.cpython-311.pyc,, +pygments/lexers/__pycache__/procfile.cpython-311.pyc,, +pygments/lexers/__pycache__/prolog.cpython-311.pyc,, +pygments/lexers/__pycache__/promql.cpython-311.pyc,, +pygments/lexers/__pycache__/python.cpython-311.pyc,, +pygments/lexers/__pycache__/q.cpython-311.pyc,, +pygments/lexers/__pycache__/qlik.cpython-311.pyc,, +pygments/lexers/__pycache__/qvt.cpython-311.pyc,, +pygments/lexers/__pycache__/r.cpython-311.pyc,, +pygments/lexers/__pycache__/rdf.cpython-311.pyc,, +pygments/lexers/__pycache__/rebol.cpython-311.pyc,, +pygments/lexers/__pycache__/resource.cpython-311.pyc,, +pygments/lexers/__pycache__/ride.cpython-311.pyc,, +pygments/lexers/__pycache__/rita.cpython-311.pyc,, +pygments/lexers/__pycache__/rnc.cpython-311.pyc,, +pygments/lexers/__pycache__/roboconf.cpython-311.pyc,, +pygments/lexers/__pycache__/robotframework.cpython-311.pyc,, +pygments/lexers/__pycache__/ruby.cpython-311.pyc,, +pygments/lexers/__pycache__/rust.cpython-311.pyc,, +pygments/lexers/__pycache__/sas.cpython-311.pyc,, +pygments/lexers/__pycache__/savi.cpython-311.pyc,, +pygments/lexers/__pycache__/scdoc.cpython-311.pyc,, +pygments/lexers/__pycache__/scripting.cpython-311.pyc,, +pygments/lexers/__pycache__/sgf.cpython-311.pyc,, +pygments/lexers/__pycache__/shell.cpython-311.pyc,, +pygments/lexers/__pycache__/sieve.cpython-311.pyc,, +pygments/lexers/__pycache__/slash.cpython-311.pyc,, +pygments/lexers/__pycache__/smalltalk.cpython-311.pyc,, +pygments/lexers/__pycache__/smithy.cpython-311.pyc,, +pygments/lexers/__pycache__/smv.cpython-311.pyc,, +pygments/lexers/__pycache__/snobol.cpython-311.pyc,, +pygments/lexers/__pycache__/solidity.cpython-311.pyc,, +pygments/lexers/__pycache__/sophia.cpython-311.pyc,, +pygments/lexers/__pycache__/special.cpython-311.pyc,, +pygments/lexers/__pycache__/spice.cpython-311.pyc,, +pygments/lexers/__pycache__/sql.cpython-311.pyc,, +pygments/lexers/__pycache__/srcinfo.cpython-311.pyc,, +pygments/lexers/__pycache__/stata.cpython-311.pyc,, +pygments/lexers/__pycache__/supercollider.cpython-311.pyc,, +pygments/lexers/__pycache__/tal.cpython-311.pyc,, +pygments/lexers/__pycache__/tcl.cpython-311.pyc,, +pygments/lexers/__pycache__/teal.cpython-311.pyc,, +pygments/lexers/__pycache__/templates.cpython-311.pyc,, +pygments/lexers/__pycache__/teraterm.cpython-311.pyc,, +pygments/lexers/__pycache__/testing.cpython-311.pyc,, +pygments/lexers/__pycache__/text.cpython-311.pyc,, +pygments/lexers/__pycache__/textedit.cpython-311.pyc,, +pygments/lexers/__pycache__/textfmts.cpython-311.pyc,, +pygments/lexers/__pycache__/theorem.cpython-311.pyc,, +pygments/lexers/__pycache__/thingsdb.cpython-311.pyc,, +pygments/lexers/__pycache__/tlb.cpython-311.pyc,, +pygments/lexers/__pycache__/tnt.cpython-311.pyc,, +pygments/lexers/__pycache__/trafficscript.cpython-311.pyc,, +pygments/lexers/__pycache__/typoscript.cpython-311.pyc,, +pygments/lexers/__pycache__/ul4.cpython-311.pyc,, +pygments/lexers/__pycache__/unicon.cpython-311.pyc,, +pygments/lexers/__pycache__/urbi.cpython-311.pyc,, +pygments/lexers/__pycache__/usd.cpython-311.pyc,, +pygments/lexers/__pycache__/varnish.cpython-311.pyc,, +pygments/lexers/__pycache__/verification.cpython-311.pyc,, +pygments/lexers/__pycache__/web.cpython-311.pyc,, +pygments/lexers/__pycache__/webassembly.cpython-311.pyc,, +pygments/lexers/__pycache__/webidl.cpython-311.pyc,, +pygments/lexers/__pycache__/webmisc.cpython-311.pyc,, +pygments/lexers/__pycache__/whiley.cpython-311.pyc,, +pygments/lexers/__pycache__/wowtoc.cpython-311.pyc,, +pygments/lexers/__pycache__/wren.cpython-311.pyc,, +pygments/lexers/__pycache__/x10.cpython-311.pyc,, +pygments/lexers/__pycache__/xorg.cpython-311.pyc,, +pygments/lexers/__pycache__/yang.cpython-311.pyc,, +pygments/lexers/__pycache__/zig.cpython-311.pyc,, +pygments/lexers/_ada_builtins.py,sha256=9BqorV9ID_Z5RLBeVbQn9aKg6j7TCk-knBgfDn9D2Ec,1543 +pygments/lexers/_asy_builtins.py,sha256=quZMwEPSom6Fs9yC9kVxUdKuSaNiwDgwzR_XiuEDE2I,27287 +pygments/lexers/_cl_builtins.py,sha256=750nWBIkaITu_ox67sfmcwEx7AuV1_m-GvbUI3im_cs,13994 +pygments/lexers/_cocoa_builtins.py,sha256=6DmhiEpFVh6TQwfLNh1c5MQ3oQAPiPRHcNpIFPSbBII,105182 +pygments/lexers/_csound_builtins.py,sha256=tmvd0MF7849I7LncGtKGIAovDW-oOQNnLU-coNqoTxI,18414 +pygments/lexers/_css_builtins.py,sha256=fghSPBeEm_bOYuzDZH03s9YymLIGzGzKOnCFDMZPeW0,12446 +pygments/lexers/_julia_builtins.py,sha256=hNIvB343zzkLq2SEnlpx7lzOj5fwYZN6Dv6upo3gus4,11883 +pygments/lexers/_lasso_builtins.py,sha256=1FXcRTixcTHwYyUTlRGebtcfsn-m7_UbKSfoAtPMtY4,134510 +pygments/lexers/_lilypond_builtins.py,sha256=YtYhV518MbcENY59RQQDygsO7MBLDMD9A2NQc4lKbfw,106781 +pygments/lexers/_lua_builtins.py,sha256=D4C-HLWIHNfq-zKdI0h6ihL9psB-Vas8HH2r7Pgr0rI,8080 +pygments/lexers/_mapping.py,sha256=lSx8z1rjmGROaWOVpDJ6PcxYkgVVnlsXfErWRwgVXcg,64980 +pygments/lexers/_mql_builtins.py,sha256=g54kSJXLwIsMGQFUbvP-bB03_S0ypjt5P8_wOmIf_3s,24713 +pygments/lexers/_mysql_builtins.py,sha256=stNurNB2CsukMyF9InuKa1zstRdbVRy0SrngsNqBsjk,25806 +pygments/lexers/_openedge_builtins.py,sha256=b9Jaw-Ji90vnrlykHfHL5rre9Lu7A3Yp1_ICTQWraLE,49398 +pygments/lexers/_php_builtins.py,sha256=8gc7AiFhgs9f1RDcMj3v7f0jtVoZZ6snHC9zJCkWgSg,107876 +pygments/lexers/_postgres_builtins.py,sha256=pr7tgitRL_ramhQCIOHIoIV9okWgrnDg2DPtAbb0lMU,12316 +pygments/lexers/_qlik_builtins.py,sha256=Z3NWn8hW3SgjsKseHjIy4n_U-Ogli0JaXSf9AAO0WDc,12595 +pygments/lexers/_scheme_builtins.py,sha256=1jEwdJB0KQhsYX9zBicy7ny0UEb0C9087kofxHW0S2s,32564 +pygments/lexers/_scilab_builtins.py,sha256=oDqV7i2FzHfn_ac6Cope9C6FRO5UC1X9aMaiCju_ZcI,52377 +pygments/lexers/_sourcemod_builtins.py,sha256=Sg7_eD5LsZxmiAn5lP0I65Fg0xffjBW_jOQRxuTL73g,26745 +pygments/lexers/_stan_builtins.py,sha256=CYZl5FHihyFyL3DT_CBePc-6JsUYx5byZuykq6gwWik,13445 +pygments/lexers/_stata_builtins.py,sha256=FlTQXy1mTrU4dtDwHLn_BgjfXgLJHCjT5ppgfSk3-9k,27227 +pygments/lexers/_tsql_builtins.py,sha256=RVFZu9_cQBjE-6tbR32zPTmf9EbAmOfcEcscCjwvv-o,15460 +pygments/lexers/_usd_builtins.py,sha256=zzudqdYKn7_bsD2UimVTGCR8oW4O9ks2k0KVw-j8hkU,1658 +pygments/lexers/_vbscript_builtins.py,sha256=0zOPR5Slupgp8ZqWTXu7FHwMDgJYzT8x44ZbRGNtVpU,4225 +pygments/lexers/_vim_builtins.py,sha256=aKqubOlfPOl0F5ge8HWjdz7WAeV_sWNyEOx94wcQQCc,57066 +pygments/lexers/actionscript.py,sha256=PjwYIKgYMtaBbzMYkLxlxcVNoGrkwVd5O63nOiKwEuE,11676 +pygments/lexers/ada.py,sha256=93CBleaaRa6uL5LcXgNKSXmggNZ74bZK4ndwjuTNlms,5320 +pygments/lexers/agile.py,sha256=95de0QM13cXvQYPrDmy-A0hfNxsqq-nSsJtCpY3DCIY,876 +pygments/lexers/algebra.py,sha256=DOKWvYgOwGlLQVJ3xKd1gym5Vte21MH2C15V6C0r3ZA,9873 +pygments/lexers/ambient.py,sha256=8f7H7fqB5lWYRt7WRiI0S8ECbOwbthnL4qS6cbJc65s,2606 +pygments/lexers/amdgpu.py,sha256=UT5-b6ililJ6BwMpoBDvjBT2vQw0jeObH57c18KzfUg,1603 +pygments/lexers/ampl.py,sha256=VxuaBx5r5ilMLY9HxzGcKr2g4FyWl9xgd_IKOrorIHc,4177 +pygments/lexers/apdlexer.py,sha256=9ZO8SSyfTqQhkDzjbOflO-kALnheHiT5lWb_S1gW-2Q,26654 +pygments/lexers/apl.py,sha256=N_9pYe_LVWI6-0HD31VmBZsF-6or3h4Q2QYp0jxY-vU,3405 +pygments/lexers/archetype.py,sha256=55YRoEaaFiehkC_UMgUYOGxDFIgbXRXfUwMzG49ufCU,11469 +pygments/lexers/arrow.py,sha256=ed8-CWv3vVPw2UDDutxGBTFeEsnX-aI2a-LRtdE0EWg,3565 +pygments/lexers/arturo.py,sha256=xrtbS2mrQPqABQSo2plP0wpSvfxoteC8l-ivs5epitA,11417 +pygments/lexers/asc.py,sha256=1JaNmliU60TOhdRtVWDNEWz2AyQavIz-lj8HiypdwZ4,1621 +pygments/lexers/asm.py,sha256=ZrFEGjh2sM7HXPcFj3GiERjVLkhrgP57SJvhONxnsOo,41243 +pygments/lexers/automation.py,sha256=MVjc_Y-gtjhF9AnMzTt1gy8ZmLmpzqzMRrLyVomL-zs,19815 +pygments/lexers/bare.py,sha256=3aSBX6Y80dSblHZkfKSch8f4rBHC9kxPC5l5t4XWtbY,3021 +pygments/lexers/basic.py,sha256=3zrIqhU10IzBV3_BSNmkvijOhvFRjHoRRWyQZmi-ORQ,27923 +pygments/lexers/bdd.py,sha256=qBXpTsbDA-4oQtFmFEUrNON5N5jDMcDCS1xPMx6V4E8,1652 +pygments/lexers/berry.py,sha256=GW5guQj9TKMhiZWusJtA8ln9uqMOwyPx-RlhULEp20c,3211 +pygments/lexers/bibtex.py,sha256=zF5J72nLbshzVi9C-bnd0ZAp6wRApFG4y-opcqCPo3k,4723 +pygments/lexers/boa.py,sha256=uU2IZb1Q0Eg38dkCvJI0sUCeptv8nyLO8svJp8VXjXY,3915 +pygments/lexers/business.py,sha256=_oNhVsxHMz8o6VSM8QNId-LgbUBpt2jQYYX4Iv73P6s,28112 +pygments/lexers/c_cpp.py,sha256=Dpu955YDV1bFzgG0vUis76w6jiL-1SJDUGzt3aVW7LA,17791 +pygments/lexers/c_like.py,sha256=2L0E2kXTDdgdxeK4MG12wbEDCz1XYrV4PqHfr7mYJeU,29206 +pygments/lexers/capnproto.py,sha256=RJMrCyFnGC-2G8ABritL4n75GYeQSDEahe3S0nJ6k-I,2175 +pygments/lexers/cddl.py,sha256=toE1wOpZM03lDcJabkcdGtnbRtzYnfaDPsls5tcCHCM,5182 +pygments/lexers/chapel.py,sha256=JqJ5oLEdg-tkOSAxxzT11dwRYGWT9asNH_5VTgd0oWk,5014 +pygments/lexers/clean.py,sha256=lVUtk21Oj4qGIbfxmNkifuVvlMvAwv_-EM0p6V3lqUY,6395 +pygments/lexers/comal.py,sha256=6jEqPDl9tB4y3mnUa-op7u5p-4fjhcJ9jZfj8pMU8tU,3156 +pygments/lexers/compiled.py,sha256=vS3om47ex-h22RRuVCuV75235Q3Mw86UJ6qJy7fQZ5Q,1407 +pygments/lexers/configs.py,sha256=p28OShOMl53sG1EQ42p9-3XN98CgseQd6bLhhvjDoR8,41825 +pygments/lexers/console.py,sha256=AWdMlp5AZ42FS3P_VUkz50bDbVjLnHmks49hXTbt_IU,4148 +pygments/lexers/cplint.py,sha256=ImcRkFnOVtvBR67V23GgXBPXZrPeCqDXEAoTnDxTFbI,1390 +pygments/lexers/crystal.py,sha256=G0f2vAs1hb1I7rVCHpcDmlmGbiFyBfBB_vn7XMOLHEY,15756 +pygments/lexers/csound.py,sha256=J3y_XkCo3YkBccdVTm51SqNWsSdQY3PqNKeDAJrdVBw,16994 +pygments/lexers/css.py,sha256=BD55SDuaHybbq2_c4CQUGLpSsF9bwZ4WxCDs1rWlwT8,25314 +pygments/lexers/d.py,sha256=p2CPwH1nlmRkL9ZOuI-ngPsOgLahHY8jsXJa47g0f6M,9875 +pygments/lexers/dalvik.py,sha256=FPrqh0Utlf2rObbwAYlMy2NHdTUWsmoZO19NfxYvkpQ,4607 +pygments/lexers/data.py,sha256=mtCLYefrhV_9DOn4C4ccwSJHHOMXof3iY1LONYBRO5s,26940 +pygments/lexers/devicetree.py,sha256=eta85yUbVbdFz2qGxQpwEWgiP_oVwL8WlLlFibswJWI,4020 +pygments/lexers/diff.py,sha256=N24UjVL9y1CAdotLVWLCsNONm6ze6zJN7Obgz5Twvkg,5164 +pygments/lexers/dotnet.py,sha256=ajyH5dwlp2phHF9HtrqryLlrEgmovHmdUlxQ46YK6tg,29696 +pygments/lexers/dsls.py,sha256=rOq1dt09hd_9bLtwNVZNy0UGzu30OFe3r0SLCCGXnJw,36774 +pygments/lexers/dylan.py,sha256=fOgYUc9mXkf6tRGd82wUrsEOAroXa2gYxzMhJzDGZsU,10380 +pygments/lexers/ecl.py,sha256=udGTA3tPw86hP_v0J5YodBURQkMx2MeMd9ixPcWdOP0,6372 +pygments/lexers/eiffel.py,sha256=S6-eiKz9qjZXk_VJE9xLa8NrEO3V0F7hdbkVtu4Zt_w,2690 +pygments/lexers/elm.py,sha256=Ic3AK6wH19iQtETVAU71cvq2bS_x9F__NIm4KlOsBXg,3152 +pygments/lexers/elpi.py,sha256=wN0qfDD2ezOt6UdaNc66la9I7bmqsW7hfNTx6ZANNc4,6370 +pygments/lexers/email.py,sha256=Ieu8fO3GapDB0lV2dI_ZnNj-us7Ts-CVlbLQrVdYXoc,4742 +pygments/lexers/erlang.py,sha256=JTjs9wXOuydAy7gNM2Uw38_o3lFRHwV7c7Cpye_J3iI,19170 +pygments/lexers/esoteric.py,sha256=72umCmQ9PjY0Clo6dZW7mnY_XnDyDExAcwicyJaiN4U,10396 +pygments/lexers/ezhil.py,sha256=ozD2yYcKYuZy8Y3vkbKEQH9TlEMcgcUsmza5D1ORHcw,3273 +pygments/lexers/factor.py,sha256=wxWEioRLplqZ3ZJpibJCctp_rEuVJVZ2Z9YcWc8wqsA,19531 +pygments/lexers/fantom.py,sha256=lunJPZfkUF9-OAi0tNcy_JR0mudGYtHVM0y0HWsJ8Vo,10197 +pygments/lexers/felix.py,sha256=H5ttiqj4qDENOPl3g0hZUDuplxA_M16JAXstmbXWALI,9646 +pygments/lexers/fift.py,sha256=7R0YDRX1XmPPzFa1Tre1VIFxh89TkACkviAMK-voCyM,1621 +pygments/lexers/floscript.py,sha256=TKZZDq6wihSZwkjk3DrRZ2TXq0bOyEgqHycaReyHseY,2668 +pygments/lexers/forth.py,sha256=UFjgCW4XsvnQYO-DXKEISPp2RvbBB2JVSd6sBrM_Sbo,7194 +pygments/lexers/fortran.py,sha256=avYkb_TXUX8QWt7rrZGyFq3Z_Oug67_x8tnh88G0HhU,10336 +pygments/lexers/foxpro.py,sha256=s1qwixRW9cCywqsVnZJ0ik_1B17lxPjEF4T0k6Xz7xo,26212 +pygments/lexers/freefem.py,sha256=5QwQX6ciIm2dW63VlAF9uD09Elsgy6drU0CCvdsv-OU,26914 +pygments/lexers/func.py,sha256=uFfm5KAI09ahrDYb6HOnIaUxjdFQ4-OhWFEGCdGxwuY,3622 +pygments/lexers/functional.py,sha256=XPPWLCDQt1LYwLXZxpTfAatB0BkjiubUiVOIHQvCjQ0,674 +pygments/lexers/futhark.py,sha256=5RlltUlCtYzAB9iAWvobVsEV4S6X_qFypT0ZxiB3tXY,3732 +pygments/lexers/gcodelexer.py,sha256=8kvQasYLSeFa7yjipOx8e0zgGGFQOJgFWLz4PtXpLt4,826 +pygments/lexers/gdscript.py,sha256=HpLvRVPYswGG2aSDSUq-JtOvDCtdyj6wkDxK-wIB9SU,7543 +pygments/lexers/go.py,sha256=m9ZZlulHhx_5D-tyrVrqOng9maHrPLYBnxxVg61PvhI,3761 +pygments/lexers/grammar_notation.py,sha256=LjsPCa5mtiVfagjiLuf9bEHZQTFW6nsbDeQYLC0kCa8,7980 +pygments/lexers/graph.py,sha256=Cm_dBin3iW31jXPy1yRq9dU0-3Zksyy-IKVlKLJF8Nc,3861 +pygments/lexers/graphics.py,sha256=m-uomUeUeQIxZAIcza4OrZ5y7UuDK9akXWVyvyxLzcI,38931 +pygments/lexers/graphviz.py,sha256=ZujNvllszspMI0BD6BaZgxO1UKZHTCM8WmQ-OmKube4,1935 +pygments/lexers/gsql.py,sha256=tbOWBBzp17gicHj5CXzY1SP8d218ubqhl56bga6bFUE,3991 +pygments/lexers/haskell.py,sha256=bsWwYungJg1QEZALjPJP3rlY2pCRSmbBYpDoNGC6FTA,32898 +pygments/lexers/haxe.py,sha256=aBXcC389HlgCTjP0cfzFlC9t0lHrXwW4uerZ4HQy3NM,30976 +pygments/lexers/hdl.py,sha256=xlunVpnsP05fMYaL1XDOFJ8lK9I_TtE48G4CI3p0uQg,22520 +pygments/lexers/hexdump.py,sha256=kuh1bgrn_zrulUZyc8vv1-7ZBppKVOZN8BaK7vTqjwU,3603 +pygments/lexers/html.py,sha256=B9LiFqh9VB9CL5BWhdi3_VcFE6tCs3__owyh6cqJP_0,19879 +pygments/lexers/idl.py,sha256=1vtdhCVNzDel2Z-xAXKfteKRPYzjqOzmVqWV2xyunVE,15450 +pygments/lexers/igor.py,sha256=cEGrCOODpPGVRJ5mQlfyOcvi0qUHwWro6X1Mm1G_1P4,30631 +pygments/lexers/inferno.py,sha256=jd1m9w-NuLYi_X-xTx_lBoXloTZ2UDVoPUKyndBgaHg,3136 +pygments/lexers/installers.py,sha256=HBZL4pkolvNL78X9hF5WFoDWA_LY9Ev0oOoBAKAVNcw,13178 +pygments/lexers/int_fiction.py,sha256=qeYrFla6FqxJedcw0IXzOciKbJP1kV2QmDpEJ7rpkPU,57119 +pygments/lexers/iolang.py,sha256=5wdWeVVgybETc94uPmLGUcsedr5viXQ0bnpNTVua0Bo,1906 +pygments/lexers/j.py,sha256=Yut2H1YqXaIobVCPd0vHPGVrqfVmDpsjAoHqvnVWPpo,4854 +pygments/lexers/javascript.py,sha256=c4Shx5iwskXf2crPPE8xwi_yEwQE-TWJfbtF_NovFlw,62859 +pygments/lexers/jmespath.py,sha256=OtJKUxa_heEFHEOcFkxfFW4iKkp9FQX3thwxJV7yeK4,2059 +pygments/lexers/jslt.py,sha256=ihdYKXtQGA6ODksKVTuujbv9pZNI_VkejgX2IRQEuq4,3701 +pygments/lexers/jsonnet.py,sha256=a2RSW6jmVHJ967m4g_ScEXDEvfVnqGQa-3feIqntgS8,5635 +pygments/lexers/julia.py,sha256=_BUPw1GQL-HlGcoXsldV6IIMfliqSqhaExxwJiDHMWY,11646 +pygments/lexers/jvm.py,sha256=GWd3TIJO4vxbkagY6P2PnlDsqBPdyssXi89XdSyiX_8,72929 +pygments/lexers/kuin.py,sha256=S-BE0wZXAixkA4HkgXEs7CX9AWS89msaeqNxacQ7Ij4,11406 +pygments/lexers/lilypond.py,sha256=C8Kaipx58ESHp25shlS0x3m1AVO6Nlmy78b8CsRjeg8,9753 +pygments/lexers/lisp.py,sha256=tvWRtYFrCuC7ZI4HEbK9hhnj_DlRARjqs1_tYBntfN4,144039 +pygments/lexers/macaulay2.py,sha256=NnhQW3v-jRTSgupYv-pCKpixQBxR8vMfPvsQAErGnNQ,31914 +pygments/lexers/make.py,sha256=XhIe6D0hx1gNm-K8lr7fFhu6Shv-kEZZubzK6wZw1VQ,7550 +pygments/lexers/markup.py,sha256=Oj38Jny-qi6fpMkx6Ww5cYAtJeK-jzKU_U2dmUYhbe0,26797 +pygments/lexers/math.py,sha256=QG4321ZoGwGMptGIUZbVkVP6nrYiKLKMocWEiYemdUI,676 +pygments/lexers/matlab.py,sha256=BmYtM5CkDfuIWVgFfLLTuzZEu2NN9P6h9Nj4zL-gvFM,132852 +pygments/lexers/maxima.py,sha256=sp3epFxSmL9aMtavhkPxHObjHCo9CUayrE5GCwPBOVc,2716 +pygments/lexers/meson.py,sha256=KGfRzEHGeurl6nmyCyvLPBp6sKgH9-dmGe89elmGzTA,4337 +pygments/lexers/mime.py,sha256=BuvfQO-eDIXWRJePQnjd_U3WQ__ViSoNHPG_nOsYi7w,7538 +pygments/lexers/minecraft.py,sha256=iw2qLM8gFrXh0XkmrmygqO1JV4UrYgBllgUMb4BnQao,13846 +pygments/lexers/mips.py,sha256=Byt2XnCaAkkLsFdutzj_JjgoHPOUSnb0IonjFDN6XbU,4604 +pygments/lexers/ml.py,sha256=X0VM9cbykg3QpAY6GyAcIFLt5BftnTMUS4VzgZWKj1k,35324 +pygments/lexers/modeling.py,sha256=-ZV9DRyDQcCcJJG0yvXD_PS_KoRd-u9zfJYnHX0d_lk,13524 +pygments/lexers/modula2.py,sha256=eeuDGPFGFTdnGRXgyvI0HGpO8Iakadc2ZOOXbeRT9Yc,53073 +pygments/lexers/monte.py,sha256=4l8-KCATmILYp2q_5Sa0gUfiwj37y_ttw99ILapY1e4,6290 +pygments/lexers/mosel.py,sha256=StaXhfFdQTKB0mSCStxmA58VSD88sJnkjwNxHmk5qmI,9187 +pygments/lexers/ncl.py,sha256=XJaGVsSHlAavTRuP8dUB9Fn7duwmTsiNRKKVL9pmhlQ,63962 +pygments/lexers/nimrod.py,sha256=kwqLEglbD7KvOcYPa4lGM2sRKCMmUXQN93Zc1WDtPls,6416 +pygments/lexers/nit.py,sha256=6hXbZnP3JabZ_VW0XrZDwruqvxavvwYwwsWqyCog0Cs,2726 +pygments/lexers/nix.py,sha256=t9hU1L9H703AAaYAJjbA24C2LRXNxOl0_TZKw3ZpcVw,4015 +pygments/lexers/oberon.py,sha256=ilMvauo5aCL_qmp0D4dOF_1QHuNNBbsJzPUwo_R1Zks,4169 +pygments/lexers/objective.py,sha256=a15atNurUMQVN-16fPVnVGk9BRwhXUOb8N_ghIwb_PY,22961 +pygments/lexers/ooc.py,sha256=0xLNAdkdaBA7ntHA5i3MKjOs0mh-9c77VQu3ELeYU4E,2982 +pygments/lexers/other.py,sha256=9-OZdK9kQ3rHtEBOnyZ6YYn0FL8evjU1X2YiDMdr72I,1744 +pygments/lexers/parasail.py,sha256=et4cbhlJ2tFgBpYPC6x_dUNHoHUVoQZEVL8cjSTuPHQ,2720 +pygments/lexers/parsers.py,sha256=lP8WV60UmS910YydAGg3utDFH6XmXMxV4YY4Ydvf8Tw,25904 +pygments/lexers/pascal.py,sha256=Gdxz8judFa9SNdaQddXbePCd6Efs-y4PhhE9gbhQuQY,30880 +pygments/lexers/pawn.py,sha256=lJXk4GDxSAmf17l9YTuogxJa2QLZP_1ClvTH1Ijx_wQ,8146 +pygments/lexers/perl.py,sha256=daB5xbHa2KNy8iPjCsQBVc0ITO3j85Tj7T5JovHmrtM,39170 +pygments/lexers/phix.py,sha256=r607k8eFtYRWRcd3jF8SOYVN76PFs7wASFZCUdshOhQ,23252 +pygments/lexers/php.py,sha256=8kg3Ag31akIHkL0jyTS0zcea0tolK6V6HhL1jPTL3tE,12505 +pygments/lexers/pointless.py,sha256=FoxCSiJKEaYKKvbZ9kA6JqUZ-v3bDcadr9KaMhoNy-U,1975 +pygments/lexers/pony.py,sha256=spaEuSzStQqCwAsMOyj8PCzkZ7yt2kzQVGUyLF89CuI,3244 +pygments/lexers/praat.py,sha256=Lni_aar1kVyTX4mjmpPJphNKCZv4Y5NiwNmHkozo-_4,12677 +pygments/lexers/procfile.py,sha256=LZO6OtDRwqaC4H0qI7ixRxOKrugOEdj-Ea6rarRfpoI,1156 +pygments/lexers/prolog.py,sha256=xtXLU_SDHAWZ47PcpW_LdxdJTQQbIt0i1YZRWiAuQ9k,12351 +pygments/lexers/promql.py,sha256=DYp4nDL_XcOqv98aProsERw1WoE_IyZdXhIAKk2oCYk,4715 +pygments/lexers/python.py,sha256=YgccSRDRVwTiSipcoGTI3JukSFokKb6psKNRSgDICbQ,53524 +pygments/lexers/q.py,sha256=GXhzLT6qbsc0LzuERBD2Gj_evoXsGZJNBhVrjOs4T0Q,6932 +pygments/lexers/qlik.py,sha256=vLcdNqAz5fGmc0sRzY4USgip-IfXUI_pq151LepCR48,3665 +pygments/lexers/qvt.py,sha256=6n_eR2ewHMH4FbXHQYMVlKxTwzlpA47vZHi6lmC774M,6072 +pygments/lexers/r.py,sha256=rVeYGy-2QPLUFYxPKsOdmKJHto226A-9zoQawR7ATiY,6185 +pygments/lexers/rdf.py,sha256=xTL6e309sP14fufIEenM2xgdYVH1MSAwUPFIPmQOCfw,15790 +pygments/lexers/rebol.py,sha256=JQZ51eBw8l5oB5-mJU8a5MghibS7on0HX3opXVMQlNI,18600 +pygments/lexers/resource.py,sha256=c5tALpcF4NXW0aaVDqMPW52mA3iA1L82QD6JUqat9sg,2902 +pygments/lexers/ride.py,sha256=05W-azkVXs-vDcUj7rPjHa0dgYADNsVrFEw-iW4Pdl8,5056 +pygments/lexers/rita.py,sha256=7e4bKm6GJ0xJVxOrxRXyWdfBvnYcWcvk4Ldnv-e-WvE,1128 +pygments/lexers/rnc.py,sha256=myQvBz1qIMlXCsk89e5bUSt9xzhWwB8xIOQ0loIZjmU,1973 +pygments/lexers/roboconf.py,sha256=7tqDyH39xHWW4BwFW3Om9EGIEKFE_Ni536-jiyU86Hw,1962 +pygments/lexers/robotframework.py,sha256=yGrjLaHIasu61f_t3YNcjU783VGoU2tS2AGrHjznsq0,18449 +pygments/lexers/ruby.py,sha256=Vx_knTQ2iQoBAlR_desRWdMQvT0De30AuHcSTnaZvvI,22775 +pygments/lexers/rust.py,sha256=vO6yuMivmnS-gMFPsjkZF3PMinR8m_kZzhiT_7zMCNs,8216 +pygments/lexers/sas.py,sha256=Qz-lJKNVIRtOnvLcApjMz3ZZnwny55EJft7nK1MRd8g,9400 +pygments/lexers/savi.py,sha256=i5i3tzl96ZElfzYlCaT8kROG7BYCOTigocHAnzDyZ28,4645 +pygments/lexers/scdoc.py,sha256=NpJ2F0Orr_ayE3OoNrF0R8o_4aCquBtqAVBiqZNvCvg,2239 +pygments/lexers/scripting.py,sha256=b4tK7psshicd8sEVPP-REXHE8Qbi_F12s_u317rAig0,70014 +pygments/lexers/sgf.py,sha256=mQxTXsHkgL3hvY1zdwppghJXVN4icKGGWqMK8_dOKzw,1986 +pygments/lexers/shell.py,sha256=MolJW_OgLwseg-9F3k3ckps0ao6T6HwoDM9lF2AlDnc,36344 +pygments/lexers/sieve.py,sha256=gYZmeIRE-RcEeJt8hELzhVKHdOKwkPaB3uFqlc5Le6M,2441 +pygments/lexers/slash.py,sha256=qfYTQ3WWkn8Pe1NmWAvehyWwJbT1juA5WUS4R3VCjmQ,8482 +pygments/lexers/smalltalk.py,sha256=JYnNJBqtimRyR9ykEsQkFplJiCLGoXW7VvA4BKXqCiQ,7206 +pygments/lexers/smithy.py,sha256=0QhvgEQ101fF1yBSBdqJFBxSGUZFy43phlMNfCTFBRs,2660 +pygments/lexers/smv.py,sha256=bXloPLxcyDntLqvKqh_Po5qe0cqYe_DOpiLRFKEV5oo,2773 +pygments/lexers/snobol.py,sha256=YBwLeRfXaT8dZTMqnzeuAsBeu7Pix0vfUlpTSLye4XY,2732 +pygments/lexers/solidity.py,sha256=9tkwj6imMH9GMG5g7iyC2rCuTMEUURW7bRxt77EgWh8,3127 +pygments/lexers/sophia.py,sha256=jHWK3Cnznj7rqbSpEIZS_GE5a_TNfNv0OC1Afy8YtFE,3330 +pygments/lexers/special.py,sha256=IwOkIlL8a555xHR7hMLWiuYVWTb-Gix19BoZ0d24LaQ,3414 +pygments/lexers/spice.py,sha256=g5f8iqkKG6UV_8BcclZWVYH-btNk6nvZW7aSyiq2UF8,2694 +pygments/lexers/sql.py,sha256=dGo7jbOcqbLSBPElnH45AkeGKhjZAvGf67euotbbGtw,34151 +pygments/lexers/srcinfo.py,sha256=7csqeQayNYiNT6HYRwfH7xVM5ZzYlrtHGDBXpojzN3A,1693 +pygments/lexers/stata.py,sha256=Bprs-_8e6BhgNuO4xl5toeL2-cPnr7TobR0X-l8svhU,6416 +pygments/lexers/supercollider.py,sha256=5TxrBNAERHERxWLeEcQoc65a0eQdk3UmMRwhpQP3CwE,3698 +pygments/lexers/tal.py,sha256=-VVpJ8R0YenDzPZjW47x8zuRkc3wGuXiynHD5_U5TaQ,2639 +pygments/lexers/tcl.py,sha256=G52CxD_OkWm4WtRc5mr51RMyngU0UoMT4DssseRdi1c,5513 +pygments/lexers/teal.py,sha256=m4yWBEetrTC-brmLt5fv6t1GEnFF8Wlb1RFYkRK5fA8,3523 +pygments/lexers/templates.py,sha256=R6O_ATWGVvMLiQJW45Z5ZHqN1mkyG-bM2ofC_y1NiOk,72695 +pygments/lexers/teraterm.py,sha256=dipxLoZEBB9L4-QPH3hcbgjr4puRO9a5_jzCz8tcoPI,9719 +pygments/lexers/testing.py,sha256=Oz6NAbYkthNO8pVbqXoFCCyg7EHZ-KziTXCGMJ8VMDI,10767 +pygments/lexers/text.py,sha256=T598C2HVyjqr3SLJOyrSaOAyvWMdRkh0KJVto4Reey4,1029 +pygments/lexers/textedit.py,sha256=9BqDTqb_yyW4kLXG8o9JJZraHuRY3G3Rjp7-m2cnGFI,7609 +pygments/lexers/textfmts.py,sha256=tB12c6K46HNlPnMkYJsOkHq1WgOU5Q9iJBRk43jR2RY,15192 +pygments/lexers/theorem.py,sha256=nudVRoSM49oG7lIqr8vtQuijaUL291YA0TlMljsZVaY,20157 +pygments/lexers/thingsdb.py,sha256=OoxmXDAFqdjDoFCTMpRFaTkcFsb2ysNqf6xu6RnnufY,4228 +pygments/lexers/tlb.py,sha256=WISgfhh5GezvA1GbEQR8PPhZTuDy4m8F9r0tmZrGR4w,1377 +pygments/lexers/tnt.py,sha256=yelC-PhBToBfpQIZiDbzKHlQasNCETMvrczpRGg2oCE,10457 +pygments/lexers/trafficscript.py,sha256=w5Mdqgijll6h8P8VfFEHtxrwgDoQVpjFQ_M9026QjBw,1474 +pygments/lexers/typoscript.py,sha256=M3CIQyFyoWtWnL4qtCgcYN6W4_Qt_oOX2tOt564q2rc,8207 +pygments/lexers/ul4.py,sha256=kAtCB6j6zZ6dSttxGyabWRBoik-Vy4TU5kIT-7zEYqg,8956 +pygments/lexers/unicon.py,sha256=UuyTkgoXTq3Kl-J5FW7rka1yrwBCvO_AFhWomNDxbR8,18512 +pygments/lexers/urbi.py,sha256=kz7TX7UPHOr_liD_FQRpBn3268KD4InI9SmPIp7BlhY,6037 +pygments/lexers/usd.py,sha256=N9MUFsDqJZzw8ZdDWYaMrX_XIKxk9T2M0402pJZpYTE,3513 +pygments/lexers/varnish.py,sha256=eHQM2dbFYi-TI_413YOFB33YhldIjrNa5uefHOPFojs,7273 +pygments/lexers/verification.py,sha256=A8BIr2VUYLJCGjKgfH5-aAxqWoph_iTzg5mbpXihMpY,3885 +pygments/lexers/web.py,sha256=hgpw-BBgmZfROb8xFI5ZNBLF12MP9fRmlI2DQ79TRYM,894 +pygments/lexers/webassembly.py,sha256=4s2yUHCgioi2KhbRiB3MCphhGxh0IJatCPKmRLp3cVY,5699 +pygments/lexers/webidl.py,sha256=-XzNoWNfCbFk7Os7y8cvjR0U7iek9cdDZrcxkxxTzCU,10517 +pygments/lexers/webmisc.py,sha256=kPeKj6PdhqEWUs5jpfRmKJQwDPDx2PbIFXwOuARx6dg,40549 +pygments/lexers/whiley.py,sha256=xbY5vJKypVrRNnfgqDAVhzSbtyFEmOnnG9YrXDnhEPc,4018 +pygments/lexers/wowtoc.py,sha256=QZD9uT1Npm1gmhsx-Lg7HuLZUYmsf_DdPlQZvqJam18,4021 +pygments/lexers/wren.py,sha256=oQaw4I2-VTnSLlQ5SDDxMT62HVZrekJbsPUbvhpkxJM,3239 +pygments/lexers/x10.py,sha256=slBniBkTAoeARUp-8Pq0Uf_P7Xw0_ZdmKPjYaazZ92Q,1920 +pygments/lexers/xorg.py,sha256=HyrsUM9WU0ktPnMn-7DYgyrPyb2xQHpClPl5O5XWLkk,902 +pygments/lexers/yang.py,sha256=360AyTrEZNSFFN4xACs4xsAls4XqgjqOvVGOX7uJJ1c,4500 +pygments/lexers/zig.py,sha256=5pzDDrS-b5QRa9LC9bxO19e7JR6nCan7JAJa1FJdbhM,3953 +pygments/modeline.py,sha256=gIbMSYrjSWPk0oATz7W9vMBYkUyTK2OcdVyKjioDRvA,986 +pygments/plugin.py,sha256=0JlFSNPQ2SXzB4IEQDnHeKlmjhsT-0ONajPUHCbShb4,2579 +pygments/regexopt.py,sha256=c6xcXGpGgvCET_3VWawJJqAnOp0QttFpQEdOPNY2Py0,3072 +pygments/scanner.py,sha256=F2T2G6cpkj-yZtzGQr-sOBw5w5-96UrJWveZN6va2aM,3092 +pygments/sphinxext.py,sha256=ZZtT7K-3_CV2894C3m-K1rZcsZPo94pcZuZfvLQy2ns,6816 +pygments/style.py,sha256=DkiPE6Gj8YmBigEnlln1cI9QVEOHbi8IY-RyVSwQbYE,6245 +pygments/styles/__init__.py,sha256=RvZV2e6sBYYz5DJ-Z3bNeMm1UEBAJBQbxkIf1-w1BAI,3395 +pygments/styles/__pycache__/__init__.cpython-311.pyc,, +pygments/styles/__pycache__/abap.cpython-311.pyc,, +pygments/styles/__pycache__/algol.cpython-311.pyc,, +pygments/styles/__pycache__/algol_nu.cpython-311.pyc,, +pygments/styles/__pycache__/arduino.cpython-311.pyc,, +pygments/styles/__pycache__/autumn.cpython-311.pyc,, +pygments/styles/__pycache__/borland.cpython-311.pyc,, +pygments/styles/__pycache__/bw.cpython-311.pyc,, +pygments/styles/__pycache__/colorful.cpython-311.pyc,, +pygments/styles/__pycache__/default.cpython-311.pyc,, +pygments/styles/__pycache__/dracula.cpython-311.pyc,, +pygments/styles/__pycache__/emacs.cpython-311.pyc,, +pygments/styles/__pycache__/friendly.cpython-311.pyc,, +pygments/styles/__pycache__/friendly_grayscale.cpython-311.pyc,, +pygments/styles/__pycache__/fruity.cpython-311.pyc,, +pygments/styles/__pycache__/gh_dark.cpython-311.pyc,, +pygments/styles/__pycache__/gruvbox.cpython-311.pyc,, +pygments/styles/__pycache__/igor.cpython-311.pyc,, +pygments/styles/__pycache__/inkpot.cpython-311.pyc,, +pygments/styles/__pycache__/lilypond.cpython-311.pyc,, +pygments/styles/__pycache__/lovelace.cpython-311.pyc,, +pygments/styles/__pycache__/manni.cpython-311.pyc,, +pygments/styles/__pycache__/material.cpython-311.pyc,, +pygments/styles/__pycache__/monokai.cpython-311.pyc,, +pygments/styles/__pycache__/murphy.cpython-311.pyc,, +pygments/styles/__pycache__/native.cpython-311.pyc,, +pygments/styles/__pycache__/nord.cpython-311.pyc,, +pygments/styles/__pycache__/onedark.cpython-311.pyc,, +pygments/styles/__pycache__/paraiso_dark.cpython-311.pyc,, +pygments/styles/__pycache__/paraiso_light.cpython-311.pyc,, +pygments/styles/__pycache__/pastie.cpython-311.pyc,, +pygments/styles/__pycache__/perldoc.cpython-311.pyc,, +pygments/styles/__pycache__/rainbow_dash.cpython-311.pyc,, +pygments/styles/__pycache__/rrt.cpython-311.pyc,, +pygments/styles/__pycache__/sas.cpython-311.pyc,, +pygments/styles/__pycache__/solarized.cpython-311.pyc,, +pygments/styles/__pycache__/staroffice.cpython-311.pyc,, +pygments/styles/__pycache__/stata_dark.cpython-311.pyc,, +pygments/styles/__pycache__/stata_light.cpython-311.pyc,, +pygments/styles/__pycache__/tango.cpython-311.pyc,, +pygments/styles/__pycache__/trac.cpython-311.pyc,, +pygments/styles/__pycache__/vim.cpython-311.pyc,, +pygments/styles/__pycache__/vs.cpython-311.pyc,, +pygments/styles/__pycache__/xcode.cpython-311.pyc,, +pygments/styles/__pycache__/zenburn.cpython-311.pyc,, +pygments/styles/abap.py,sha256=7QsS8lOQh_M3dr76KQ3-RaYIBfEWeNosEgMsBvu_2QU,705 +pygments/styles/algol.py,sha256=kz5ILXpiYKJF11_4xOPxpG91Wm8zGt6RBqiL3sH3oRQ,2216 +pygments/styles/algol_nu.py,sha256=_-Pl6edSeuJDt5XLQLwXPhy85nYhnjKiMTSFtMZ-2O8,2231 +pygments/styles/arduino.py,sha256=xydJymOW4pLjX_ZUuyRpKNL0Cpbi-dGNT3-VQUW9wu4,4443 +pygments/styles/autumn.py,sha256=Se4tPjreFfhX3XskKNGUEdvfobNL-itd5qO33-zsrZg,2096 +pygments/styles/borland.py,sha256=6hivq7_FxLDJoXXy-2HBsojFca8b7ulblJCYt8LbTUs,1514 +pygments/styles/bw.py,sha256=QcIDiosv20OYBe0D5mu1KZx6ll6L7w06__IUsivjvH0,1308 +pygments/styles/colorful.py,sha256=K2uHGWEd3fO5bAhHbp_5Z3FBTioHlZFSbPsvvvCKjbc,2730 +pygments/styles/default.py,sha256=Y2OCPkQo96Nhut9JJ5gKiSUdw34jZyF0mztYKt8sdHk,2488 +pygments/styles/dracula.py,sha256=n5y1aKUaRVeFAyQUhzCYfAYAYb_mB0qGNNHbOoEgMvI,3314 +pygments/styles/emacs.py,sha256=hPlFDlCx5uOxQS2-lfJ_iWp7t6NzKzV8pG-nRL4QaDY,2439 +pygments/styles/friendly.py,sha256=lk-aZIv6oU6BcP1nuzk7YnCyfT8N09zvGPLTijZaptU,2502 +pygments/styles/friendly_grayscale.py,sha256=jU9YccoY0WEZbLnyQ6Ms_GD9UU9nIxyYJ01cOK3vK74,2707 +pygments/styles/fruity.py,sha256=YSLME-2ldk806w2q8KwPEwJG_lUwbDjgUOON6GWqPOQ,1274 +pygments/styles/gh_dark.py,sha256=1uTXQQLge1c0sSlYdWpO8E-oHZpA3cZtp8lg697V_5c,3481 +pygments/styles/gruvbox.py,sha256=wXEvbg9oUVIberD9NK09HqxXZF9hJZU_wWID6N91auM,3230 +pygments/styles/igor.py,sha256=HShSIPfYFBjb7-_OKS1ybrJ7LGFAYf-mIbbFVORONvg,692 +pygments/styles/inkpot.py,sha256=PuYymWU-qaLZlJUvm9IV8apx0cud8JyTUMMc7yKVZes,2302 +pygments/styles/lilypond.py,sha256=fvNi0XzR4Y_nikorTzBBiACkrgbb6XKWrElT4x6fwhQ,2016 +pygments/styles/lovelace.py,sha256=O1B_qFnjWqMvjPkmrdJjP6L14C1nodQst_TkbcsQPzQ,3117 +pygments/styles/manni.py,sha256=0wijm_0LctgoBpeJFR_spuriSafmxEm_r8AQLGwiMJc,2350 +pygments/styles/material.py,sha256=DenZF_lrw1qy9vYyb78VJMRQd9WvjgZvkuquG7Pjcno,4083 +pygments/styles/monokai.py,sha256=WXuj6DE33quDFva-6iG2h0UPXJd9AttKRsQCKzsFFvA,5063 +pygments/styles/murphy.py,sha256=XBQ8cnbj8-UJ1BPSrVZ71RJLg4cnG_27G3JMEJhH3q8,2703 +pygments/styles/native.py,sha256=4dM251hEDZSHpn3TDmKESTw4ipkKhJbjUEGoioiqsdU,1948 +pygments/styles/nord.py,sha256=XgccTxZtXlQg04u4ZTrsFBviCoqqCEp2REiAj5flQbM,5244 +pygments/styles/onedark.py,sha256=to_IrmgcfyPsjRiikCDi3ApkDXMrtmv1C_zA7DmF4Ok,1664 +pygments/styles/paraiso_dark.py,sha256=wqFMXgju9IpuODurCHwdRpyE0vzZCh3sjvEfIy_elvU,5526 +pygments/styles/paraiso_light.py,sha256=1EbYrcy_zP081a0iad2x2jE7789F28T8TSVD_A5eb0Y,5530 +pygments/styles/pastie.py,sha256=hT4VrEWQiTK26ewereBf124SQ6nvBRXQU1QQa8DGb-I,2425 +pygments/styles/perldoc.py,sha256=3hw_EUgbnm_JhvMhDZq7B54oRhIM1N8hsovlZIqUF_k,2128 +pygments/styles/rainbow_dash.py,sha256=tRcpKBqufUNlbtbxWuZhlXEMg98kUcWlIfIvBvgt098,2432 +pygments/styles/rrt.py,sha256=MgLkoeLMjoyqEd52hZSutH54LzPQG7fPAcyAG8iDo9I,874 +pygments/styles/sas.py,sha256=NhE8FA1mbbleQlJnC6m7sQVBKrOE1vSqbPc3v7c6Lno,1393 +pygments/styles/solarized.py,sha256=fgBJq12PRG1UspBft3r0fMdZxFjI9Etm8XfiTNdiZRg,4078 +pygments/styles/staroffice.py,sha256=YLgK5QjuZndITzDTlzbQs5tztp-p2-5MbUJ2XY7aX00,770 +pygments/styles/stata_dark.py,sha256=tWF-PomZTIodRJaZVbv1gZK_PAkFr86qQwzAq0_xC4M,1198 +pygments/styles/stata_light.py,sha256=VBSqRQQJJWOHOwpInZ0ClSRETRCcTRARJ3-PWzZ92D0,1227 +pygments/styles/tango.py,sha256=rAVsYXJtnDwf1Zpy8JCC5zasEiUx-XNemZGe8kqBGC8,7039 +pygments/styles/trac.py,sha256=Ota0CWC6xlZmXWoDWlhQAe71ruww_ZURrOAXZSI6Xfk,1885 +pygments/styles/vim.py,sha256=Z3D9OFwGm8LhgIgzlwL4nBcke8t2YtL1R93k3mx_RJk,1922 +pygments/styles/vs.py,sha256=rZHvA7M5hjZ1ZHB1zMP06Ktn6MU55_8z6AyHCTFXxxM,1026 +pygments/styles/xcode.py,sha256=SyX3Rh4_g5F_Vz9ntWDhSFuBerPp_tj4j3VrMSG2zcE,1453 +pygments/styles/zenburn.py,sha256=1EF8-K8mbl4Ooe0idwQo0rZOVXSaZK5m3HJrVT7HkZk,2148 +pygments/token.py,sha256=vA2yNHGJBHfq4jNQSah7C9DmIOp34MmYHPA8P-cYAHI,6184 +pygments/unistring.py,sha256=gP3gK-6C4oAFjjo9HvoahsqzuV4Qz0jl0E0OxfDerHI,63187 +pygments/util.py,sha256=KgwpWWC3By5AiNwxGTI7oI9aXupH2TyZWukafBJe0Mg,9110 diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/WHEEL new file mode 100644 index 00000000..57e3d840 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/entry_points.txt b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/entry_points.txt new file mode 100644 index 00000000..15498e35 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +pygmentize = pygments.cmdline:main diff --git a/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/top_level.txt new file mode 100644 index 00000000..a9f49e01 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/Pygments-2.14.0.dist-info/top_level.txt @@ -0,0 +1 @@ +pygments diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/LICENSE new file mode 100644 index 00000000..a343a622 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright 2012-2018 Dmitry Shachnev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/METADATA b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/METADATA new file mode 100644 index 00000000..f0323fd1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/METADATA @@ -0,0 +1,114 @@ +Metadata-Version: 2.1 +Name: SecretStorage +Version: 3.3.3 +Summary: Python bindings to FreeDesktop.org Secret Service API +Home-page: https://github.com/mitya57/secretstorage +Author: Dmitry Shachnev +Author-email: mitya57@gmail.com +License: BSD 3-Clause License +Platform: Linux +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Security +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: cryptography (>=2.0) +Requires-Dist: jeepney (>=0.6) + +.. image:: https://github.com/mitya57/secretstorage/workflows/tests/badge.svg + :target: https://github.com/mitya57/secretstorage/actions + :alt: GitHub Actions status +.. image:: https://codecov.io/gh/mitya57/secretstorage/branch/master/graph/badge.svg + :target: https://codecov.io/gh/mitya57/secretstorage + :alt: Coverage status +.. image:: https://readthedocs.org/projects/secretstorage/badge/?version=latest + :target: https://secretstorage.readthedocs.io/en/latest/ + :alt: ReadTheDocs status + +Module description +================== + +This module provides a way for securely storing passwords and other secrets. + +It uses D-Bus `Secret Service`_ API that is supported by GNOME Keyring, +KWallet (since version 5.97) and KeePassXC. + +The main classes provided are ``secretstorage.Item``, representing a secret +item (that has a *label*, a *secret* and some *attributes*) and +``secretstorage.Collection``, a place items are stored in. + +SecretStorage supports most of the functions provided by Secret Service, +including creating and deleting items and collections, editing items, +locking and unlocking collections (asynchronous unlocking is also supported). + +The documentation can be found on `secretstorage.readthedocs.io`_. + +.. _`Secret Service`: https://specifications.freedesktop.org/secret-service/ +.. _`secretstorage.readthedocs.io`: https://secretstorage.readthedocs.io/en/latest/ + +Building the module +=================== + +.. note:: + SecretStorage 3.x supports Python 3.6 and newer versions. + If you have an older version of Python, install SecretStorage 2.x:: + + pip install "SecretStorage < 3" + +SecretStorage requires these packages to work: + +* Jeepney_ +* `python-cryptography`_ + +To build SecretStorage, use this command:: + + python3 setup.py build + +If you have Sphinx_ installed, you can also build the documentation:: + + python3 setup.py build_sphinx + +.. _Jeepney: https://pypi.org/project/jeepney/ +.. _`python-cryptography`: https://pypi.org/project/cryptography/ +.. _Sphinx: http://sphinx-doc.org/ + +Testing the module +================== + +First, make sure that you have the Secret Service daemon installed. +The `GNOME Keyring`_ is the reference server-side implementation for the +Secret Service specification. + +.. _`GNOME Keyring`: https://download.gnome.org/sources/gnome-keyring/ + +Then, start the daemon and unlock the ``default`` collection, if needed. +The testsuite will fail to run if the ``default`` collection exists and is +locked. If it does not exist, the testsuite can also use the temporary +``session`` collection, as provided by the GNOME Keyring. + +Then, run the Python unittest module:: + + python3 -m unittest discover -s tests + +If you want to run the tests in an isolated or headless environment, run +this command in a D-Bus session:: + + dbus-run-session -- python3 -m unittest discover -s tests + +Get the code +============ + +SecretStorage is available under BSD license. The source code can be found +on GitHub_. + +.. _GitHub: https://github.com/mitya57/secretstorage diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/RECORD b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/RECORD new file mode 100644 index 00000000..63b1c6f7 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/RECORD @@ -0,0 +1,22 @@ +SecretStorage-3.3.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SecretStorage-3.3.3.dist-info/LICENSE,sha256=cPa_yndjPDXvohgyjtpUhtcFTCkU1hggmA43h5dSCiU,1504 +SecretStorage-3.3.3.dist-info/METADATA,sha256=ZScD5voEgjme04wlw9OZigESMxLa2xG_eaIeZ_IMqJI,4027 +SecretStorage-3.3.3.dist-info/RECORD,, +SecretStorage-3.3.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +SecretStorage-3.3.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +SecretStorage-3.3.3.dist-info/top_level.txt,sha256=hveSi1OWGaEt3kEVbjmZ0M-ASPxi6y-nTPVa-d3c0B4,14 +secretstorage/__init__.py,sha256=W1p-HB1Qh12Dv6K8ml0Wj_MzN09_dEeezJjQZAHf-O4,3364 +secretstorage/__pycache__/__init__.cpython-311.pyc,, +secretstorage/__pycache__/collection.cpython-311.pyc,, +secretstorage/__pycache__/defines.cpython-311.pyc,, +secretstorage/__pycache__/dhcrypto.cpython-311.pyc,, +secretstorage/__pycache__/exceptions.cpython-311.pyc,, +secretstorage/__pycache__/item.cpython-311.pyc,, +secretstorage/__pycache__/util.cpython-311.pyc,, +secretstorage/collection.py,sha256=lHwSOkFO5sRspgcUBoBI8ZG2au2bcUSO6X64ksVdnsQ,9436 +secretstorage/defines.py,sha256=DzUrEWzSvBlN8kK2nVXnLGlCZv7HWNyfN1AYqRmjKGE,807 +secretstorage/dhcrypto.py,sha256=BiuDoNvNmd8i7Ul4ENouRnbqFE3SUmTUSAn6RVvn7Tg,2578 +secretstorage/exceptions.py,sha256=1uUZXTua4jRZf4PKDIT2SVWcSKP2lP97s8r3eJZudio,1655 +secretstorage/item.py,sha256=3niFSjOzwrB2hV1jrg78RXgBsTrpw44852VpZHXUpeE,5813 +secretstorage/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +secretstorage/util.py,sha256=vHu01QaooMQ5sRdRDFX9pg7rrzfJWF9vg0plm3Zg0Po,6755 diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/WHEEL new file mode 100644 index 00000000..becc9a66 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/top_level.txt new file mode 100644 index 00000000..0ec6ae86 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/SecretStorage-3.3.3.dist-info/top_level.txt @@ -0,0 +1 @@ +secretstorage diff --git a/.venv/lib/python3.11/site-packages/_black_version.py b/.venv/lib/python3.11/site-packages/_black_version.py new file mode 100644 index 00000000..358412d9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_black_version.py @@ -0,0 +1 @@ +version = "23.1.0" diff --git a/.venv/lib/python3.11/site-packages/_cffi_backend.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/_cffi_backend.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..d940aac0d8f7d2a8e6f88a33606a7a320692043c GIT binary patch literal 332440 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.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= + +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK. + +INSTALL/DEBUGGING +================= + +To include this support in another application that has setup.py generated +scripts: + +- Add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point. + +- Include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + Call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument(). + +If things do not work right away: + +- Switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 + +- Run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not. + +- Sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" + +from __future__ import annotations + +import argparse +from glob import glob +import os +import sys +from typing import Any + + +class FastFilesCompleter: + """Fast file completer class.""" + + def __init__(self, directories: bool = True) -> None: + self.directories = directories + + def __call__(self, prefix: str, **kwargs: Any) -> list[str]: + # Only called on non option completions. + if os.sep in prefix[1:]: + prefix_dir = len(os.path.dirname(prefix) + os.sep) + else: + prefix_dir = 0 + completion = [] + globbed = [] + if "*" not in prefix and "?" not in prefix: + # We are on unix, otherwise no bash. + if not prefix or prefix[-1] == os.sep: + globbed.extend(glob(prefix + ".*")) + prefix += "*" + globbed.extend(glob(prefix)) + for x in sorted(globbed): + if os.path.isdir(x): + x += "/" + # Append stripping the prefix (like bash, not like compgen). + completion.append(x[prefix_dir:]) + return completion + + +if os.environ.get("_ARGCOMPLETE"): + try: + import argcomplete.completers + except ImportError: + sys.exit(-1) + filescompleter: FastFilesCompleter | None = FastFilesCompleter() + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + argcomplete.autocomplete(parser, always_complete_options=False) + +else: + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + pass + + filescompleter = None diff --git a/.venv/lib/python3.11/site-packages/_pytest/_code/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/_code/__init__.py new file mode 100644 index 00000000..7f67a2e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,26 @@ +"""Python inspection/code generation API.""" + +from __future__ import annotations + +from .code import Code +from .code import ExceptionInfo +from .code import filter_traceback +from .code import Frame +from .code import getfslineno +from .code import Traceback +from .code import TracebackEntry +from .source import getrawcode +from .source import Source + + +__all__ = [ + "Code", + "ExceptionInfo", + "Frame", + "Source", + "Traceback", + "TracebackEntry", + "filter_traceback", + "getfslineno", + "getrawcode", +] diff --git a/.venv/lib/python3.11/site-packages/_pytest/_code/code.py b/.venv/lib/python3.11/site-packages/_pytest/_code/code.py new file mode 100644 index 00000000..f1241f14 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_code/code.py @@ -0,0 +1,1567 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import ast +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import inspect +from inspect import CO_VARARGS +from inspect import CO_VARKEYWORDS +from io import StringIO +import os +from pathlib import Path +import re +import sys +from traceback import extract_tb +from traceback import format_exception +from traceback import format_exception_only +from traceback import FrameSummary +from types import CodeType +from types import FrameType +from types import TracebackType +from typing import Any +from typing import ClassVar +from typing import Final +from typing import final +from typing import Generic +from typing import Literal +from typing import overload +from typing import SupportsIndex +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +import pluggy + +import _pytest +from _pytest._code.source import findsource +from _pytest._code.source import getrawcode +from _pytest._code.source import getstatementrange_ast +from _pytest._code.source import Source +from _pytest._io import TerminalWriter +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr +from _pytest.compat import get_real_func +from _pytest.deprecated import check_ispytest +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + +TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] + +EXCEPTION_OR_MORE = Union[type[BaseException], tuple[type[BaseException], ...]] + + +class Code: + """Wrapper around Python code objects.""" + + __slots__ = ("raw",) + + def __init__(self, obj: CodeType) -> None: + self.raw = obj + + @classmethod + def from_function(cls, obj: object) -> Code: + return cls(getrawcode(obj)) + + def __eq__(self, other): + return self.raw == other.raw + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def firstlineno(self) -> int: + return self.raw.co_firstlineno - 1 + + @property + def name(self) -> str: + return self.raw.co_name + + @property + def path(self) -> Path | str: + """Return a path object pointing to source code, or an ``str`` in + case of ``OSError`` / non-existing file.""" + if not self.raw.co_filename: + return "" + try: + p = absolutepath(self.raw.co_filename) + # maybe don't try this checking + if not p.exists(): + raise OSError("path check failed.") + return p + except OSError: + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? + return self.raw.co_filename + + @property + def fullsource(self) -> Source | None: + """Return a _pytest._code.Source object for the full source file of the code.""" + full, _ = findsource(self.raw) + return full + + def source(self) -> Source: + """Return a _pytest._code.Source object for the code object's source only.""" + # return source only for that part of code + return Source(self.raw) + + def getargs(self, var: bool = False) -> tuple[str, ...]: + """Return a tuple with the argument names for the code object. + + If 'var' is set True also return the names of the variable and + keyword arguments when present. + """ + # Handy shortcut for getting args. + raw = self.raw + argcount = raw.co_argcount + if var: + argcount += raw.co_flags & CO_VARARGS + argcount += raw.co_flags & CO_VARKEYWORDS + return raw.co_varnames[:argcount] + + +class Frame: + """Wrapper around a Python frame holding f_locals and f_globals + in which expressions can be evaluated.""" + + __slots__ = ("raw",) + + def __init__(self, frame: FrameType) -> None: + self.raw = frame + + @property + def lineno(self) -> int: + return self.raw.f_lineno - 1 + + @property + def f_globals(self) -> dict[str, Any]: + return self.raw.f_globals + + @property + def f_locals(self) -> dict[str, Any]: + return self.raw.f_locals + + @property + def code(self) -> Code: + return Code(self.raw.f_code) + + @property + def statement(self) -> Source: + """Statement this frame is at.""" + if self.code.fullsource is None: + return Source("") + return self.code.fullsource.getstatement(self.lineno) + + def eval(self, code, **vars): + """Evaluate 'code' in the frame. + + 'vars' are optional additional local variables. + + Returns the result of the evaluation. + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + return eval(code, self.f_globals, f_locals) + + def repr(self, object: object) -> str: + """Return a 'safe' (non-recursive, one-line) string repr for 'object'.""" + return saferepr(object) + + def getargs(self, var: bool = False): + """Return a list of tuples (name, value) for all arguments. + + If 'var' is set True, also include the variable and keyword arguments + when present. + """ + retval = [] + for arg in self.code.getargs(var): + try: + retval.append((arg, self.f_locals[arg])) + except KeyError: + pass # this can occur when using Psyco + return retval + + +class TracebackEntry: + """A single entry in a Traceback.""" + + __slots__ = ("_rawentry", "_repr_style") + + def __init__( + self, + rawentry: TracebackType, + repr_style: Literal["short", "long"] | None = None, + ) -> None: + self._rawentry: Final = rawentry + self._repr_style: Final = repr_style + + def with_repr_style( + self, repr_style: Literal["short", "long"] | None + ) -> TracebackEntry: + return TracebackEntry(self._rawentry, repr_style) + + @property + def lineno(self) -> int: + return self._rawentry.tb_lineno - 1 + + def get_python_framesummary(self) -> FrameSummary: + # Python's built-in traceback module implements all the nitty gritty + # details to get column numbers of out frames. + stack_summary = extract_tb(self._rawentry, limit=1) + return stack_summary[0] + + # Column and end line numbers introduced in python 3.11 + if sys.version_info < (3, 11): + + @property + def end_lineno_relative(self) -> int | None: + return None + + @property + def colno(self) -> int | None: + return None + + @property + def end_colno(self) -> int | None: + return None + else: + + @property + def end_lineno_relative(self) -> int | None: + frame_summary = self.get_python_framesummary() + if frame_summary.end_lineno is None: # pragma: no cover + return None + return frame_summary.end_lineno - 1 - self.frame.code.firstlineno + + @property + def colno(self) -> int | None: + """Starting byte offset of the expression in the traceback entry.""" + return self.get_python_framesummary().colno + + @property + def end_colno(self) -> int | None: + """Ending byte offset of the expression in the traceback entry.""" + return self.get_python_framesummary().end_colno + + @property + def frame(self) -> Frame: + return Frame(self._rawentry.tb_frame) + + @property + def relline(self) -> int: + return self.lineno - self.frame.code.firstlineno + + def __repr__(self) -> str: + return f"" + + @property + def statement(self) -> Source: + """_pytest._code.Source object for the current statement.""" + source = self.frame.code.fullsource + assert source is not None + return source.getstatement(self.lineno) + + @property + def path(self) -> Path | str: + """Path to the source code.""" + return self.frame.code.path + + @property + def locals(self) -> dict[str, Any]: + """Locals of underlying frame.""" + return self.frame.f_locals + + def getfirstlinesource(self) -> int: + return self.frame.code.firstlineno + + def getsource( + self, astcache: dict[str | Path, ast.AST] | None = None + ) -> Source | None: + """Return failing source code.""" + # we use the passed in astcache to not reparse asttrees + # within exception info printing + source = self.frame.code.fullsource + if source is None: + return None + key = astnode = None + if astcache is not None: + key = self.frame.code.path + if key is not None: + astnode = astcache.get(key, None) + start = self.getfirstlinesource() + try: + astnode, _, end = getstatementrange_ast( + self.lineno, source, astnode=astnode + ) + except SyntaxError: + end = self.lineno + 1 + else: + if key is not None and astcache is not None: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self, excinfo: ExceptionInfo[BaseException] | None) -> bool: + """Return True if the current frame has a var __tracebackhide__ + resolving to True. + + If __tracebackhide__ is a callable, it gets called with the + ExceptionInfo instance and can decide whether to hide the traceback. + + Mostly for internal use. + """ + tbh: bool | Callable[[ExceptionInfo[BaseException] | None], bool] = False + for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): + # in normal cases, f_locals and f_globals are dictionaries + # however via `exec(...)` / `eval(...)` they can be other types + # (even incorrect types!). + # as such, we suppress all exceptions while accessing __tracebackhide__ + try: + tbh = maybe_ns_dct["__tracebackhide__"] + except Exception: + pass + else: + break + if tbh and callable(tbh): + return tbh(excinfo) + return tbh + + def __str__(self) -> str: + name = self.frame.code.name + try: + line = str(self.statement).lstrip() + except KeyboardInterrupt: + raise + except BaseException: + line = "???" + # This output does not quite match Python's repr for traceback entries, + # but changing it to do so would break certain plugins. See + # https://github.com/pytest-dev/pytest/pull/7535/ for details. + return f" File '{self.path}':{self.lineno + 1} in {name}\n {line}\n" + + @property + def name(self) -> str: + """co_name of underlying code.""" + return self.frame.code.raw.co_name + + +class Traceback(list[TracebackEntry]): + """Traceback objects encapsulate and offer higher level access to Traceback entries.""" + + def __init__( + self, + tb: TracebackType | Iterable[TracebackEntry], + ) -> None: + """Initialize from given python traceback object and ExceptionInfo.""" + if isinstance(tb, TracebackType): + + def f(cur: TracebackType) -> Iterable[TracebackEntry]: + cur_: TracebackType | None = cur + while cur_ is not None: + yield TracebackEntry(cur_) + cur_ = cur_.tb_next + + super().__init__(f(tb)) + else: + super().__init__(tb) + + def cut( + self, + path: os.PathLike[str] | str | None = None, + lineno: int | None = None, + firstlineno: int | None = None, + excludepath: os.PathLike[str] | None = None, + ) -> Traceback: + """Return a Traceback instance wrapping part of this Traceback. + + By providing any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined. + + This allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback). + """ + path_ = None if path is None else os.fspath(path) + excludepath_ = None if excludepath is None else os.fspath(excludepath) + for x in self: + code = x.frame.code + codepath = code.path + if path is not None and str(codepath) != path_: + continue + if ( + excludepath is not None + and isinstance(codepath, Path) + and excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator] + ): + continue + if lineno is not None and x.lineno != lineno: + continue + if firstlineno is not None and x.frame.code.firstlineno != firstlineno: + continue + return Traceback(x._rawentry) + return self + + @overload + def __getitem__(self, key: SupportsIndex) -> TracebackEntry: ... + + @overload + def __getitem__(self, key: slice) -> Traceback: ... + + def __getitem__(self, key: SupportsIndex | slice) -> TracebackEntry | Traceback: + if isinstance(key, slice): + return self.__class__(super().__getitem__(key)) + else: + return super().__getitem__(key) + + def filter( + self, + excinfo_or_fn: ExceptionInfo[BaseException] | Callable[[TracebackEntry], bool], + /, + ) -> Traceback: + """Return a Traceback instance with certain items removed. + + If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s + which are hidden (see ishidden() above). + + Otherwise, the filter is a function that gets a single argument, a + ``TracebackEntry`` instance, and should return True when the item should + be added to the ``Traceback``, False when not. + """ + if isinstance(excinfo_or_fn, ExceptionInfo): + fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731 + else: + fn = excinfo_or_fn + return Traceback(filter(fn, self)) + + def recursionindex(self) -> int | None: + """Return the index of the frame/TracebackEntry where recursion originates if + appropriate, None if no recursion occurred.""" + cache: dict[tuple[Any, int, int], list[dict[str, Any]]] = {} + for i, entry in enumerate(self): + # id for the code.raw is needed to work around + # the strange metaprogramming in the decorator lib from pypi + # which generates code objects that have hash/value equality + # XXX needs a test + key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno + values = cache.setdefault(key, []) + # Since Python 3.13 f_locals is a proxy, freeze it. + loc = dict(entry.frame.f_locals) + if values: + for otherloc in values: + if otherloc == loc: + return i + values.append(loc) + return None + + +def stringify_exception( + exc: BaseException, include_subexception_msg: bool = True +) -> str: + try: + notes = getattr(exc, "__notes__", []) + except KeyError: + # Workaround for https://github.com/python/cpython/issues/98778 on + # Python <= 3.9, and some 3.10 and 3.11 patch versions. + HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) + if sys.version_info < (3, 12) and isinstance(exc, HTTPError): + notes = [] + else: # pragma: no cover + # exception not related to above bug, reraise + raise + if not include_subexception_msg and isinstance(exc, BaseExceptionGroup): + message = exc.message + else: + message = str(exc) + + return "\n".join( + [ + message, + *notes, + ] + ) + + +E = TypeVar("E", bound=BaseException, covariant=True) + + +@final +@dataclasses.dataclass +class ExceptionInfo(Generic[E]): + """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" + + _assert_start_repr: ClassVar = "AssertionError('assert " + + _excinfo: tuple[type[E], E, TracebackType] | None + _striptext: str + _traceback: Traceback | None + + def __init__( + self, + excinfo: tuple[type[E], E, TracebackType] | None, + striptext: str = "", + traceback: Traceback | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._excinfo = excinfo + self._striptext = striptext + self._traceback = traceback + + @classmethod + def from_exception( + cls, + # Ignoring error: "Cannot use a covariant type variable as a parameter". + # This is OK to ignore because this class is (conceptually) readonly. + # See https://github.com/python/mypy/issues/7049. + exception: E, # type: ignore[misc] + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: + """Return an ExceptionInfo for an existing exception. + + The exception must have a non-``None`` ``__traceback__`` attribute, + otherwise this function fails with an assertion error. This means that + the exception must have been raised, or added a traceback with the + :py:meth:`~BaseException.with_traceback()` method. + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + + .. versionadded:: 7.4 + """ + assert exception.__traceback__, ( + "Exceptions passed to ExcInfo.from_exception(...)" + " must have a non-None __traceback__." + ) + exc_info = (type(exception), exception, exception.__traceback__) + return cls.from_exc_info(exc_info, exprinfo) + + @classmethod + def from_exc_info( + cls, + exc_info: tuple[type[E], E, TracebackType], + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: + """Like :func:`from_exception`, but using old-style exc_info tuple.""" + _striptext = "" + if exprinfo is None and isinstance(exc_info[1], AssertionError): + exprinfo = getattr(exc_info[1], "msg", None) + if exprinfo is None: + exprinfo = saferepr(exc_info[1]) + if exprinfo and exprinfo.startswith(cls._assert_start_repr): + _striptext = "AssertionError: " + + return cls(exc_info, _striptext, _ispytest=True) + + @classmethod + def from_current(cls, exprinfo: str | None = None) -> ExceptionInfo[BaseException]: + """Return an ExceptionInfo matching the current traceback. + + .. warning:: + + Experimental API + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + """ + tup = sys.exc_info() + assert tup[0] is not None, "no current exception" + assert tup[1] is not None, "no current exception" + assert tup[2] is not None, "no current exception" + exc_info = (tup[0], tup[1], tup[2]) + return ExceptionInfo.from_exc_info(exc_info, exprinfo) + + @classmethod + def for_later(cls) -> ExceptionInfo[E]: + """Return an unfilled ExceptionInfo.""" + return cls(None, _ispytest=True) + + def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None: + """Fill an unfilled ExceptionInfo created with ``for_later()``.""" + assert self._excinfo is None, "ExceptionInfo was already filled" + self._excinfo = exc_info + + @property + def type(self) -> type[E]: + """The exception class.""" + assert self._excinfo is not None, ( + ".type can only be used after the context manager exits" + ) + return self._excinfo[0] + + @property + def value(self) -> E: + """The exception value.""" + assert self._excinfo is not None, ( + ".value can only be used after the context manager exits" + ) + return self._excinfo[1] + + @property + def tb(self) -> TracebackType: + """The exception raw traceback.""" + assert self._excinfo is not None, ( + ".tb can only be used after the context manager exits" + ) + return self._excinfo[2] + + @property + def typename(self) -> str: + """The type name of the exception.""" + assert self._excinfo is not None, ( + ".typename can only be used after the context manager exits" + ) + return self.type.__name__ + + @property + def traceback(self) -> Traceback: + """The traceback.""" + if self._traceback is None: + self._traceback = Traceback(self.tb) + return self._traceback + + @traceback.setter + def traceback(self, value: Traceback) -> None: + self._traceback = value + + def __repr__(self) -> str: + if self._excinfo is None: + return "" + return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" + + def exconly(self, tryshort: bool = False) -> str: + """Return the exception as a string. + + When 'tryshort' resolves to True, and the exception is an + AssertionError, only the actual exception part of the exception + representation is returned (so 'AssertionError: ' is removed from + the beginning). + """ + + def _get_single_subexc( + eg: BaseExceptionGroup[BaseException], + ) -> BaseException | None: + if len(eg.exceptions) != 1: + return None + if isinstance(e := eg.exceptions[0], BaseExceptionGroup): + return _get_single_subexc(e) + return e + + if ( + tryshort + and isinstance(self.value, BaseExceptionGroup) + and (subexc := _get_single_subexc(self.value)) is not None + ): + return f"{subexc!r} [single exception in {type(self.value).__name__}]" + + lines = format_exception_only(self.type, self.value) + text = "".join(lines) + text = text.rstrip() + if tryshort: + if text.startswith(self._striptext): + text = text[len(self._striptext) :] + return text + + def errisinstance(self, exc: EXCEPTION_OR_MORE) -> bool: + """Return True if the exception is an instance of exc. + + Consider using ``isinstance(excinfo.value, exc)`` instead. + """ + return isinstance(self.value, exc) + + def _getreprcrash(self) -> ReprFileLocation | None: + # Find last non-hidden traceback entry that led to the exception of the + # traceback, or None if all hidden. + for i in range(-1, -len(self.traceback) - 1, -1): + entry = self.traceback[i] + if not entry.ishidden(self): + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + exconly = self.exconly(tryshort=True) + return ReprFileLocation(path, lineno + 1, exconly) + return None + + def getrepr( + self, + showlocals: bool = False, + style: TracebackStyle = "long", + abspath: bool = False, + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] = True, + funcargs: bool = False, + truncate_locals: bool = True, + truncate_args: bool = True, + chain: bool = True, + ) -> ReprExceptionInfo | ExceptionChainRepr: + """Return str()able representation of this exception info. + + :param bool showlocals: + Show locals per traceback entry. + Ignored if ``style=="native"``. + + :param str style: + long|short|line|no|native|value traceback style. + + :param bool abspath: + If paths should be changed to absolute or left unchanged. + + :param tbfilter: + A filter for traceback entries. + + * If false, don't hide any entries. + * If true, hide internal entries and entries that contain a local + variable ``__tracebackhide__ = True``. + * If a callable, delegates the filtering to the callable. + + Ignored if ``style`` is ``"native"``. + + :param bool funcargs: + Show fixtures ("funcargs" for legacy purposes) per traceback entry. + + :param bool truncate_locals: + With ``showlocals==True``, make sure locals can be safely represented as strings. + + :param bool truncate_args: + With ``showargs==True``, make sure args can be safely represented as strings. + + :param bool chain: + If chained exceptions in Python 3 should be shown. + + .. versionchanged:: 3.9 + + Added the ``chain`` parameter. + """ + if style == "native": + return ReprExceptionInfo( + reprtraceback=ReprTracebackNative( + format_exception( + self.type, + self.value, + self.traceback[0]._rawentry if self.traceback else None, + ) + ), + reprcrash=self._getreprcrash(), + ) + + fmt = FormattedExcinfo( + showlocals=showlocals, + style=style, + abspath=abspath, + tbfilter=tbfilter, + funcargs=funcargs, + truncate_locals=truncate_locals, + truncate_args=truncate_args, + chain=chain, + ) + return fmt.repr_excinfo(self) + + def match(self, regexp: str | re.Pattern[str]) -> Literal[True]: + """Check whether the regular expression `regexp` matches the string + representation of the exception using :func:`python:re.search`. + + If it matches `True` is returned, otherwise an `AssertionError` is raised. + """ + __tracebackhide__ = True + value = stringify_exception(self.value) + msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" + if regexp == value: + msg += "\n Did you mean to `re.escape()` the regex?" + assert re.search(regexp, value), msg + # Return True to allow for "assert excinfo.match()". + return True + + def _group_contains( + self, + exc_group: BaseExceptionGroup[BaseException], + expected_exception: EXCEPTION_OR_MORE, + match: str | re.Pattern[str] | None, + target_depth: int | None = None, + current_depth: int = 1, + ) -> bool: + """Return `True` if a `BaseExceptionGroup` contains a matching exception.""" + if (target_depth is not None) and (current_depth > target_depth): + # already descended past the target depth + return False + for exc in exc_group.exceptions: + if isinstance(exc, BaseExceptionGroup): + if self._group_contains( + exc, expected_exception, match, target_depth, current_depth + 1 + ): + return True + if (target_depth is not None) and (current_depth != target_depth): + # not at the target depth, no match + continue + if not isinstance(exc, expected_exception): + continue + if match is not None: + value = stringify_exception(exc) + if not re.search(match, value): + continue + return True + return False + + def group_contains( + self, + expected_exception: EXCEPTION_OR_MORE, + *, + match: str | re.Pattern[str] | None = None, + depth: int | None = None, + ) -> bool: + """Check whether a captured exception group contains a matching exception. + + :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception: + The expected exception type, or a tuple if one of multiple possible + exception types are expected. + + :param str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception and its `PEP-678 ` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + :param Optional[int] depth: + If `None`, will search for a matching exception at any nesting depth. + If >= 1, will only match an exception if it's at the specified depth (depth = 1 being + the exceptions contained within the topmost exception group). + + .. versionadded:: 8.0 + + .. warning:: + This helper makes it easy to check for the presence of specific exceptions, + but it is very bad for checking that the group does *not* contain + *any other exceptions*. + You should instead consider using :class:`pytest.RaisesGroup` + + """ + msg = "Captured exception is not an instance of `BaseExceptionGroup`" + assert isinstance(self.value, BaseExceptionGroup), msg + msg = "`depth` must be >= 1 if specified" + assert (depth is None) or (depth >= 1), msg + return self._group_contains(self.value, expected_exception, match, depth) + + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + # Type alias for the `tbfilter` setting: + # bool: If True, it should be filtered using Traceback.filter() + # callable: A callable that takes an ExceptionInfo and returns the filtered traceback. + TracebackFilter: TypeAlias = Union[ + bool, Callable[[ExceptionInfo[BaseException]], Traceback] + ] + + +@dataclasses.dataclass +class FormattedExcinfo: + """Presenting information about failing Functions and Generators.""" + + # for traceback entries + flow_marker: ClassVar = ">" + fail_marker: ClassVar = "E" + + showlocals: bool = False + style: TracebackStyle = "long" + abspath: bool = True + tbfilter: TracebackFilter = True + funcargs: bool = False + truncate_locals: bool = True + truncate_args: bool = True + chain: bool = True + astcache: dict[str | Path, ast.AST] = dataclasses.field( + default_factory=dict, init=False, repr=False + ) + + def _getindent(self, source: Source) -> int: + # Figure out indent for the given source. + try: + s = str(source.getstatement(len(source) - 1)) + except KeyboardInterrupt: + raise + except BaseException: + try: + s = str(source[-1]) + except KeyboardInterrupt: + raise + except BaseException: + return 0 + return 4 + (len(s) - len(s.lstrip())) + + def _getentrysource(self, entry: TracebackEntry) -> Source | None: + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def repr_args(self, entry: TracebackEntry) -> ReprFuncArgs | None: + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + if self.truncate_args: + str_repr = saferepr(argvalue) + else: + str_repr = saferepr(argvalue, maxsize=None) + args.append((argname, str_repr)) + return ReprFuncArgs(args) + return None + + def get_source( + self, + source: Source | None, + line_index: int = -1, + excinfo: ExceptionInfo[BaseException] | None = None, + short: bool = False, + end_line_index: int | None = None, + colno: int | None = None, + end_colno: int | None = None, + ) -> list[str]: + """Return formatted and marked up source lines.""" + lines = [] + if source is not None and line_index < 0: + line_index += len(source) + if source is None or line_index >= len(source.lines) or line_index < 0: + # `line_index` could still be outside `range(len(source.lines))` if + # we're processing AST with pathological position attributes. + source = Source("???") + line_index = 0 + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + lines.extend( + self.get_highlight_arrows_for_line( + raw_line=source.raw_lines[line_index], + line=source.lines[line_index].strip(), + lineno=line_index, + end_lineno=end_line_index, + colno=colno, + end_colno=end_colno, + ) + ) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + lines.extend( + self.get_highlight_arrows_for_line( + raw_line=source.raw_lines[line_index], + line=source.lines[line_index], + lineno=line_index, + end_lineno=end_line_index, + colno=colno, + end_colno=end_colno, + ) + ) + for line in source.lines[line_index + 1 :]: + lines.append(space_prefix + line) + if excinfo is not None: + indent = 4 if short else self._getindent(source) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines + + def get_highlight_arrows_for_line( + self, + line: str, + raw_line: str, + lineno: int | None, + end_lineno: int | None, + colno: int | None, + end_colno: int | None, + ) -> list[str]: + """Return characters highlighting a source line. + + Example with colno and end_colno pointing to the bar expression: + "foo() + bar()" + returns " ^^^^^" + """ + if lineno != end_lineno: + # Don't handle expressions that span multiple lines. + return [] + if colno is None or end_colno is None: + # Can't do anything without column information. + return [] + + num_stripped_chars = len(raw_line) - len(line) + + start_char_offset = _byte_offset_to_character_offset(raw_line, colno) + end_char_offset = _byte_offset_to_character_offset(raw_line, end_colno) + num_carets = end_char_offset - start_char_offset + # If the highlight would span the whole line, it is redundant, don't + # show it. + if num_carets >= len(line.strip()): + return [] + + highlights = " " + highlights += " " * (start_char_offset - num_stripped_chars + 1) + highlights += "^" * num_carets + return [highlights] + + def get_exconly( + self, + excinfo: ExceptionInfo[BaseException], + indent: int = 4, + markall: bool = False, + ) -> list[str]: + lines = [] + indentstr = " " * indent + # Get the real exception information out. + exlines = excinfo.exconly(tryshort=True).split("\n") + failindent = self.fail_marker + indentstr[1:] + for line in exlines: + lines.append(failindent + line) + if not markall: + failindent = indentstr + return lines + + def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None: + if self.showlocals: + lines = [] + keys = [loc for loc in locals if loc[0] != "@"] + keys.sort() + for name in keys: + value = locals[name] + if name == "__builtins__": + lines.append("__builtins__ = ") + else: + # This formatting could all be handled by the + # _repr() function, which is only reprlib.Repr in + # disguise, so is very configurable. + if self.truncate_locals: + str_repr = saferepr(value) + else: + str_repr = safeformat(value) + # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): + lines.append(f"{name:<10} = {str_repr}") + # else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) + return None + + def repr_traceback_entry( + self, + entry: TracebackEntry | None, + excinfo: ExceptionInfo[BaseException] | None = None, + ) -> ReprEntry: + lines: list[str] = [] + style = ( + entry._repr_style + if entry is not None and entry._repr_style is not None + else self.style + ) + if style in ("short", "long") and entry is not None: + source = self._getentrysource(entry) + if source is None: + source = Source("???") + line_index = 0 + end_line_index, colno, end_colno = None, None, None + else: + line_index = entry.relline + end_line_index = entry.end_lineno_relative + colno = entry.colno + end_colno = entry.end_colno + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source( + source=source, + line_index=line_index, + excinfo=excinfo, + short=short, + end_line_index=end_line_index, + colno=colno, + end_colno=end_colno, + ) + lines.extend(s) + if short: + message = f"in {entry.name}" + else: + message = (excinfo and excinfo.typename) or "" + entry_path = entry.path + path = self._makepath(entry_path) + reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) + elif style == "value": + if excinfo: + lines.extend(str(excinfo.value).split("\n")) + return ReprEntry(lines, None, None, None, style) + else: + if excinfo: + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None, None, style) + + def _makepath(self, path: Path | str) -> str: + if not self.abspath and isinstance(path, Path): + try: + np = bestrelpath(Path.cwd(), path) + except OSError: + return str(path) + if len(np) < len(str(path)): + return np + return str(path) + + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> ReprTraceback: + traceback = filter_excinfo_traceback(self.tbfilter, excinfo) + + if isinstance(excinfo.value, RecursionError): + traceback, extraline = self._truncate_recursive_traceback(traceback) + else: + extraline = None + + if not traceback: + if extraline is None: + extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames." + entries = [self.repr_traceback_entry(None, excinfo)] + return ReprTraceback(entries, extraline, style=self.style) + + last = traceback[-1] + if self.style == "value": + entries = [self.repr_traceback_entry(last, excinfo)] + return ReprTraceback(entries, None, style=self.style) + + entries = [ + self.repr_traceback_entry(entry, excinfo if last == entry else None) + for entry in traceback + ] + return ReprTraceback(entries, extraline, style=self.style) + + def _truncate_recursive_traceback( + self, traceback: Traceback + ) -> tuple[Traceback, str | None]: + """Truncate the given recursive traceback trying to find the starting + point of the recursion. + + The detection is done by going through each traceback entry and + finding the point in which the locals of the frame are equal to the + locals of a previous frame (see ``recursionindex()``). + + Handle the situation where the recursion process might raise an + exception (for example comparing numpy arrays using equality raises a + TypeError), in which case we do our best to warn the user of the + error and show a limited traceback. + """ + try: + recursionindex = traceback.recursionindex() + except Exception as e: + max_frames = 10 + extraline: str | None = ( + "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" + " The following exception happened when comparing locals in the stack frame:\n" + f" {type(e).__name__}: {e!s}\n" + f" Displaying first and last {max_frames} stack frames out of {len(traceback)}." + ) + # Type ignored because adding two instances of a List subtype + # currently incorrectly has type List instead of the subtype. + traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore + else: + if recursionindex is not None: + extraline = "!!! Recursion detected (same locals & position)" + traceback = traceback[: recursionindex + 1] + else: + extraline = None + + return traceback, extraline + + def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr: + repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] + e: BaseException | None = excinfo.value + excinfo_: ExceptionInfo[BaseException] | None = excinfo + descr = None + seen: set[int] = set() + while e is not None and id(e) not in seen: + seen.add(id(e)) + + if excinfo_: + # Fall back to native traceback as a temporary workaround until + # full support for exception groups added to ExceptionInfo. + # See https://github.com/pytest-dev/pytest/issues/9159 + reprtraceback: ReprTraceback | ReprTracebackNative + if isinstance(e, BaseExceptionGroup): + # don't filter any sub-exceptions since they shouldn't have any internal frames + traceback = filter_excinfo_traceback(self.tbfilter, excinfo) + reprtraceback = ReprTracebackNative( + format_exception( + type(excinfo.value), + excinfo.value, + traceback[0]._rawentry, + ) + ) + else: + reprtraceback = self.repr_traceback(excinfo_) + reprcrash = excinfo_._getreprcrash() + else: + # Fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work. + reprtraceback = ReprTracebackNative(format_exception(type(e), e, None)) + reprcrash = None + repr_chain += [(reprtraceback, reprcrash, descr)] + + if e.__cause__ is not None and self.chain: + e = e.__cause__ + excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None + descr = "The above exception was the direct cause of the following exception:" + elif ( + e.__context__ is not None and not e.__suppress_context__ and self.chain + ): + e = e.__context__ + excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None + descr = "During handling of the above exception, another exception occurred:" + else: + e = None + repr_chain.reverse() + return ExceptionChainRepr(repr_chain) + + +@dataclasses.dataclass(eq=False) +class TerminalRepr: + def __str__(self) -> str: + # FYI this is called from pytest-xdist's serialization of exception + # information. + io = StringIO() + tw = TerminalWriter(file=io) + self.toterminal(tw) + return io.getvalue().strip() + + def __repr__(self) -> str: + return f"<{self.__class__} instance at {id(self):0x}>" + + def toterminal(self, tw: TerminalWriter) -> None: + raise NotImplementedError() + + +# This class is abstract -- only subclasses are instantiated. +@dataclasses.dataclass(eq=False) +class ExceptionRepr(TerminalRepr): + # Provided by subclasses. + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None + sections: list[tuple[str, str, str]] = dataclasses.field( + init=False, default_factory=list + ) + + def addsection(self, name: str, content: str, sep: str = "-") -> None: + self.sections.append((name, content, sep)) + + def toterminal(self, tw: TerminalWriter) -> None: + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) + + +@dataclasses.dataclass(eq=False) +class ExceptionChainRepr(ExceptionRepr): + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]] + + def __init__( + self, + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]], + ) -> None: + # reprcrash and reprtraceback of the outermost (the newest) exception + # in the chain. + super().__init__( + reprtraceback=chain[-1][0], + reprcrash=chain[-1][1], + ) + self.chain = chain + + def toterminal(self, tw: TerminalWriter) -> None: + for element in self.chain: + element[0].toterminal(tw) + if element[2] is not None: + tw.line("") + tw.line(element[2], yellow=True) + super().toterminal(tw) + + +@dataclasses.dataclass(eq=False) +class ReprExceptionInfo(ExceptionRepr): + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None + + def toterminal(self, tw: TerminalWriter) -> None: + self.reprtraceback.toterminal(tw) + super().toterminal(tw) + + +@dataclasses.dataclass(eq=False) +class ReprTraceback(TerminalRepr): + reprentries: Sequence[ReprEntry | ReprEntryNative] + extraline: str | None + style: TracebackStyle + + entrysep: ClassVar = "_ " + + def toterminal(self, tw: TerminalWriter) -> None: + # The entries might have different styles. + for i, entry in enumerate(self.reprentries): + if entry.style == "long": + tw.line("") + entry.toterminal(tw) + if i < len(self.reprentries) - 1: + next_entry = self.reprentries[i + 1] + if entry.style == "long" or ( + entry.style == "short" and next_entry.style == "long" + ): + tw.sep(self.entrysep) + + if self.extraline: + tw.line(self.extraline) + + +class ReprTracebackNative(ReprTraceback): + def __init__(self, tblines: Sequence[str]) -> None: + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + self.style = "native" + + +@dataclasses.dataclass(eq=False) +class ReprEntryNative(TerminalRepr): + lines: Sequence[str] + + style: ClassVar[TracebackStyle] = "native" + + def toterminal(self, tw: TerminalWriter) -> None: + tw.write("".join(self.lines)) + + +@dataclasses.dataclass(eq=False) +class ReprEntry(TerminalRepr): + lines: Sequence[str] + reprfuncargs: ReprFuncArgs | None + reprlocals: ReprLocals | None + reprfileloc: ReprFileLocation | None + style: TracebackStyle + + def _write_entry_lines(self, tw: TerminalWriter) -> None: + """Write the source code portions of a list of traceback entries with syntax highlighting. + + Usually entries are lines like these: + + " x = 1" + "> assert x == 2" + "E assert 1 == 2" + + This function takes care of rendering the "source" portions of it (the lines without + the "E" prefix) using syntax highlighting, taking care to not highlighting the ">" + character, as doing so might break line continuations. + """ + if not self.lines: + return + + if self.style == "value": + # Using tw.write instead of tw.line for testing purposes due to TWMock implementation; + # lines written with TWMock.line and TWMock._write_source cannot be distinguished + # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE + for line in self.lines: + tw.write(line) + tw.write("\n") + return + + # separate indents and source lines that are not failures: we want to + # highlight the code but not the indentation, which may contain markers + # such as "> assert 0" + fail_marker = f"{FormattedExcinfo.fail_marker} " + indent_size = len(fail_marker) + indents: list[str] = [] + source_lines: list[str] = [] + failure_lines: list[str] = [] + for index, line in enumerate(self.lines): + is_failure_line = line.startswith(fail_marker) + if is_failure_line: + # from this point on all lines are considered part of the failure + failure_lines.extend(self.lines[index:]) + break + else: + indents.append(line[:indent_size]) + source_lines.append(line[indent_size:]) + + tw._write_source(source_lines, indents) + + # failure lines are always completely red and bold + for line in failure_lines: + tw.line(line, bold=True, red=True) + + def toterminal(self, tw: TerminalWriter) -> None: + if self.style == "short": + if self.reprfileloc: + self.reprfileloc.toterminal(tw) + self._write_entry_lines(tw) + if self.reprlocals: + self.reprlocals.toterminal(tw, indent=" " * 8) + return + + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) + + self._write_entry_lines(tw) + + if self.reprlocals: + tw.line("") + self.reprlocals.toterminal(tw) + if self.reprfileloc: + if self.lines: + tw.line("") + self.reprfileloc.toterminal(tw) + + def __str__(self) -> str: + return "{}\n{}\n{}".format( + "\n".join(self.lines), self.reprlocals, self.reprfileloc + ) + + +@dataclasses.dataclass(eq=False) +class ReprFileLocation(TerminalRepr): + path: str + lineno: int + message: str + + def __post_init__(self) -> None: + self.path = str(self.path) + + def toterminal(self, tw: TerminalWriter) -> None: + # Filename and lineno output for each entry, using an output format + # that most editors understand. + msg = self.message + i = msg.find("\n") + if i != -1: + msg = msg[:i] + tw.write(self.path, bold=True, red=True) + tw.line(f":{self.lineno}: {msg}") + + +@dataclasses.dataclass(eq=False) +class ReprLocals(TerminalRepr): + lines: Sequence[str] + + def toterminal(self, tw: TerminalWriter, indent="") -> None: + for line in self.lines: + tw.line(indent + line) + + +@dataclasses.dataclass(eq=False) +class ReprFuncArgs(TerminalRepr): + args: Sequence[tuple[str, object]] + + def toterminal(self, tw: TerminalWriter) -> None: + if self.args: + linesofar = "" + for name, value in self.args: + ns = f"{name} = {value}" + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") + + +def getfslineno(obj: object) -> tuple[str | Path, int]: + """Return source location (path, lineno) for the given object. + + If the source cannot be determined return ("", -1). + + The line number is 0-based. + """ + # xxx let decorators etc specify a sane ordering + # NOTE: this used to be done in _pytest.compat.getfslineno, initially added + # in 6ec13a2b9. It ("place_as") appears to be something very custom. + obj = get_real_func(obj) + if hasattr(obj, "place_as"): + obj = obj.place_as + + try: + code = Code.from_function(obj) + except TypeError: + try: + fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] + except TypeError: + return "", -1 + + fspath = (fn and absolutepath(fn)) or "" + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except OSError: + pass + return fspath, lineno + + return code.path, code.firstlineno + + +def _byte_offset_to_character_offset(str, offset): + """Converts a byte based offset in a string to a code-point.""" + as_utf8 = str.encode("utf-8") + return len(as_utf8[:offset].decode("utf-8", errors="replace")) + + +# Relative paths that we use to filter traceback entries from appearing to the user; +# see filter_traceback. +# note: if we need to add more paths than what we have now we should probably use a list +# for better maintenance. + +_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc")) +# pluggy is either a package or a single module depending on the version +if _PLUGGY_DIR.name == "__init__.py": + _PLUGGY_DIR = _PLUGGY_DIR.parent +_PYTEST_DIR = Path(_pytest.__file__).parent + + +def filter_traceback(entry: TracebackEntry) -> bool: + """Return True if a TracebackEntry instance should be included in tracebacks. + + We hide traceback entries of: + + * dynamically generated code (no code to show up for it); + * internal traceback from pytest or its internal libraries, py and pluggy. + """ + # entry.path might sometimes return a str object when the entry + # points to dynamically generated code. + # See https://bitbucket.org/pytest-dev/py/issues/71. + raw_filename = entry.frame.code.raw.co_filename + is_generated = "<" in raw_filename and ">" in raw_filename + if is_generated: + return False + + # entry.path might point to a non-existing file, in which case it will + # also return a str object. See #1133. + p = Path(entry.path) + + parents = p.parents + if _PLUGGY_DIR in parents: + return False + if _PYTEST_DIR in parents: + return False + + return True + + +def filter_excinfo_traceback( + tbfilter: TracebackFilter, excinfo: ExceptionInfo[BaseException] +) -> Traceback: + """Filter the exception traceback in ``excinfo`` according to ``tbfilter``.""" + if callable(tbfilter): + return tbfilter(excinfo) + elif tbfilter: + return excinfo.traceback.filter(excinfo) + else: + return excinfo.traceback diff --git a/.venv/lib/python3.11/site-packages/_pytest/_code/source.py b/.venv/lib/python3.11/site-packages/_pytest/_code/source.py new file mode 100644 index 00000000..a8f7201a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_code/source.py @@ -0,0 +1,225 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import ast +from bisect import bisect_right +from collections.abc import Iterable +from collections.abc import Iterator +import inspect +import textwrap +import tokenize +import types +from typing import overload +import warnings + + +class Source: + """An immutable object holding a source code fragment. + + When using Source(...), the source lines are deindented. + """ + + def __init__(self, obj: object = None) -> None: + if not obj: + self.lines: list[str] = [] + self.raw_lines: list[str] = [] + elif isinstance(obj, Source): + self.lines = obj.lines + self.raw_lines = obj.raw_lines + elif isinstance(obj, (tuple, list)): + self.lines = deindent(x.rstrip("\n") for x in obj) + self.raw_lines = list(x.rstrip("\n") for x in obj) + elif isinstance(obj, str): + self.lines = deindent(obj.split("\n")) + self.raw_lines = obj.split("\n") + else: + try: + rawcode = getrawcode(obj) + src = inspect.getsource(rawcode) + except TypeError: + src = inspect.getsource(obj) # type: ignore[arg-type] + self.lines = deindent(src.split("\n")) + self.raw_lines = src.split("\n") + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Source): + return NotImplemented + return self.lines == other.lines + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @overload + def __getitem__(self, key: int) -> str: ... + + @overload + def __getitem__(self, key: slice) -> Source: ... + + def __getitem__(self, key: int | slice) -> str | Source: + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + newsource = Source() + newsource.lines = self.lines[key.start : key.stop] + newsource.raw_lines = self.raw_lines[key.start : key.stop] + return newsource + + def __iter__(self) -> Iterator[str]: + return iter(self.lines) + + def __len__(self) -> int: + return len(self.lines) + + def strip(self) -> Source: + """Return new Source object with trailing and leading blank lines removed.""" + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end - 1].strip(): + end -= 1 + source = Source() + source.raw_lines = self.raw_lines + source.lines[:] = self.lines[start:end] + return source + + def indent(self, indent: str = " " * 4) -> Source: + """Return a copy of the source object with all lines indented by the + given indent-string.""" + newsource = Source() + newsource.raw_lines = self.raw_lines + newsource.lines = [(indent + line) for line in self.lines] + return newsource + + def getstatement(self, lineno: int) -> Source: + """Return Source statement which contains the given linenumber + (counted from 0).""" + start, end = self.getstatementrange(lineno) + return self[start:end] + + def getstatementrange(self, lineno: int) -> tuple[int, int]: + """Return (start, end) tuple which spans the minimal statement region + which containing the given lineno.""" + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self) -> Source: + """Return a new Source object deindented.""" + newsource = Source() + newsource.lines[:] = deindent(self.lines) + newsource.raw_lines = self.raw_lines + return newsource + + def __str__(self) -> str: + return "\n".join(self.lines) + + +# +# helper functions +# + + +def findsource(obj) -> tuple[Source | None, int]: + try: + sourcelines, lineno = inspect.findsource(obj) + except Exception: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + source.raw_lines = sourcelines + return source, lineno + + +def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: + """Return code object for given function.""" + try: + return obj.__code__ # type: ignore[attr-defined,no-any-return] + except AttributeError: + pass + if trycall: + call = getattr(obj, "__call__", None) + if call and not isinstance(obj, type): + return getrawcode(call, trycall=False) + raise TypeError(f"could not get code object for {obj!r}") + + +def deindent(lines: Iterable[str]) -> list[str]: + return textwrap.dedent("\n".join(lines)).splitlines() + + +def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]: + # Flatten all statements and except handlers into one lineno-list. + # AST's line numbers start indexing at 1. + values: list[int] = [] + for x in ast.walk(node): + if isinstance(x, (ast.stmt, ast.ExceptHandler)): + # The lineno points to the class/def, so need to include the decorators. + if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): + for d in x.decorator_list: + values.append(d.lineno - 1) + values.append(x.lineno - 1) + for name in ("finalbody", "orelse"): + val: list[ast.stmt] | None = getattr(x, name, None) + if val: + # Treat the finally/orelse part as its own statement. + values.append(val[0].lineno - 1 - 1) + values.sort() + insert_index = bisect_right(values, lineno) + start = values[insert_index - 1] + if insert_index >= len(values): + end = None + else: + end = values[insert_index] + return start, end + + +def getstatementrange_ast( + lineno: int, + source: Source, + assertion: bool = False, + astnode: ast.AST | None = None, +) -> tuple[ast.AST, int, int]: + if astnode is None: + content = str(source) + # See #4260: + # Don't produce duplicate warnings when compiling source to find AST. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + astnode = ast.parse(content, "source", "exec") + + start, end = get_statement_startend2(lineno, astnode) + # We need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # Make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself. + block_finder = inspect.BlockFinder() + # If we start with an indented line, put blockfinder to "started" mode. + block_finder.started = ( + bool(source.lines[start]) and source.lines[start][0].isspace() + ) + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # The end might still point to a comment or empty line, correct it. + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end diff --git a/.venv/lib/python3.11/site-packages/_pytest/_io/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/_io/__init__.py new file mode 100644 index 00000000..b0155b18 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_io/__init__.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from .terminalwriter import get_terminal_width +from .terminalwriter import TerminalWriter + + +__all__ = [ + "TerminalWriter", + "get_terminal_width", +] diff --git a/.venv/lib/python3.11/site-packages/_pytest/_io/pprint.py b/.venv/lib/python3.11/site-packages/_pytest/_io/pprint.py new file mode 100644 index 00000000..28f06909 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_io/pprint.py @@ -0,0 +1,673 @@ +# mypy: allow-untyped-defs +# This module was imported from the cpython standard library +# (https://github.com/python/cpython/) at commit +# c5140945c723ae6c4b7ee81ff720ac8ea4b52cfd (python3.12). +# +# +# Original Author: Fred L. Drake, Jr. +# fdrake@acm.org +# +# This is a simple little module I wrote to make life easier. I didn't +# see anything quite like it in the library, though I may have overlooked +# something. I wrote this when I was trying to read some heavily nested +# tuples with fairly non-descriptive content. This is modeled very much +# after Lisp/Scheme - style pretty-printing of lists. If you find it +# useful, thank small children who sleep at night. +from __future__ import annotations + +import collections as _collections +from collections.abc import Callable +from collections.abc import Iterator +import dataclasses as _dataclasses +from io import StringIO as _StringIO +import re +import types as _types +from typing import Any +from typing import IO + + +class _safe_key: + """Helper function for key functions when sorting unorderable objects. + + The wrapped-object will fallback to a Py2.x style comparison for + unorderable types (sorting first comparing the type name and then by + the obj ids). Does not work recursively, so dict.items() must have + _safe_key applied to both the key and the value. + + """ + + __slots__ = ["obj"] + + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + try: + return self.obj < other.obj + except TypeError: + return (str(type(self.obj)), id(self.obj)) < ( + str(type(other.obj)), + id(other.obj), + ) + + +def _safe_tuple(t): + """Helper function for comparing 2-tuples""" + return _safe_key(t[0]), _safe_key(t[1]) + + +class PrettyPrinter: + def __init__( + self, + indent: int = 4, + width: int = 80, + depth: int | None = None, + ) -> None: + """Handle pretty printing operations onto a stream using a set of + configured parameters. + + indent + Number of spaces to indent for each level of nesting. + + width + Attempted maximum number of columns in the output. + + depth + The maximum depth to print out nested structures. + + """ + if indent < 0: + raise ValueError("indent must be >= 0") + if depth is not None and depth <= 0: + raise ValueError("depth must be > 0") + if not width: + raise ValueError("width must be != 0") + self._depth = depth + self._indent_per_level = indent + self._width = width + + def pformat(self, object: Any) -> str: + sio = _StringIO() + self._format(object, sio, 0, 0, set(), 0) + return sio.getvalue() + + def _format( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + objid = id(object) + if objid in context: + stream.write(_recursion(object)) + return + + p = self._dispatch.get(type(object).__repr__, None) + if p is not None: + context.add(objid) + p(self, object, stream, indent, allowance, context, level + 1) + context.remove(objid) + elif ( + _dataclasses.is_dataclass(object) + and not isinstance(object, type) + and object.__dataclass_params__.repr # type:ignore[attr-defined] + and + # Check dataclass has generated repr method. + hasattr(object.__repr__, "__wrapped__") + and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ + ): + context.add(objid) + self._pprint_dataclass( + object, stream, indent, allowance, context, level + 1 + ) + context.remove(objid) + else: + stream.write(self._repr(object, context, level)) + + def _pprint_dataclass( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + cls_name = object.__class__.__name__ + items = [ + (f.name, getattr(object, f.name)) + for f in _dataclasses.fields(object) + if f.repr + ] + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch: dict[ + Callable[..., str], + Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None], + ] = {} + + def _pprint_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("{") + items = sorted(object.items(), key=_safe_tuple) + self._format_dict_items(items, stream, indent, allowance, context, level) + write("}") + + _dispatch[dict.__repr__] = _pprint_dict + + def _pprint_ordered_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + "(") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict + + def _pprint_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("[") + self._format_items(object, stream, indent, allowance, context, level) + stream.write("]") + + _dispatch[list.__repr__] = _pprint_list + + def _pprint_tuple( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("(") + self._format_items(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[tuple.__repr__] = _pprint_tuple + + def _pprint_set( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + typ = object.__class__ + if typ is set: + stream.write("{") + endchar = "}" + else: + stream.write(typ.__name__ + "({") + endchar = "})" + object = sorted(object, key=_safe_key) + self._format_items(object, stream, indent, allowance, context, level) + stream.write(endchar) + + _dispatch[set.__repr__] = _pprint_set + _dispatch[frozenset.__repr__] = _pprint_set + + def _pprint_str( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if not len(object): + write(repr(object)) + return + chunks = [] + lines = object.splitlines(True) + if level == 1: + indent += 1 + allowance += 1 + max_width1 = max_width = self._width - indent + for i, line in enumerate(lines): + rep = repr(line) + if i == len(lines) - 1: + max_width1 -= allowance + if len(rep) <= max_width1: + chunks.append(rep) + else: + # A list of alternating (non-space, space) strings + parts = re.findall(r"\S*\s*", line) + assert parts + assert not parts[-1] + parts.pop() # drop empty last part + max_width2 = max_width + current = "" + for j, part in enumerate(parts): + candidate = current + part + if j == len(parts) - 1 and i == len(lines) - 1: + max_width2 -= allowance + if len(repr(candidate)) > max_width2: + if current: + chunks.append(repr(current)) + current = part + else: + current = candidate + if current: + chunks.append(repr(current)) + if len(chunks) == 1: + write(rep) + return + if level == 1: + write("(") + for i, rep in enumerate(chunks): + if i > 0: + write("\n" + " " * indent) + write(rep) + if level == 1: + write(")") + + _dispatch[str.__repr__] = _pprint_str + + def _pprint_bytes( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if len(object) <= 4: + write(repr(object)) + return + parens = level == 1 + if parens: + indent += 1 + allowance += 1 + write("(") + delim = "" + for rep in _wrap_bytes_repr(object, self._width - indent, allowance): + write(delim) + write(rep) + if not delim: + delim = "\n" + " " * indent + if parens: + write(")") + + _dispatch[bytes.__repr__] = _pprint_bytes + + def _pprint_bytearray( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("bytearray(") + self._pprint_bytes( + bytes(object), stream, indent + 10, allowance + 1, context, level + 1 + ) + write(")") + + _dispatch[bytearray.__repr__] = _pprint_bytearray + + def _pprint_mappingproxy( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("mappingproxy(") + self._format(object.copy(), stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy + + def _pprint_simplenamespace( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if type(object) is _types.SimpleNamespace: + # The SimpleNamespace repr is "namespace" instead of the class + # name, so we do the same here. For subclasses; use the class name. + cls_name = "namespace" + else: + cls_name = object.__class__.__name__ + items = object.__dict__.items() + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace + + def _format_dict_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(self._repr(key, context, level)) + write(": ") + self._format(ent, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _format_namespace_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(key) + write("=") + if id(ent) in context: + # Special-case representation of recursion to match standard + # recursive dataclass repr. + write("...") + else: + self._format( + ent, + stream, + item_indent + len(key) + 1, + 1, + context, + level, + ) + + write(",") + + write("\n" + " " * indent) + + def _format_items( + self, + items: list[Any], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + + for item in items: + write(delimnl) + self._format(item, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _repr(self, object: Any, context: set[int], level: int) -> str: + return self._safe_repr(object, context.copy(), self._depth, level) + + def _pprint_default_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + rdf = self._repr(object.default_factory, context, level) + stream.write(f"{object.__class__.__name__}({rdf}, ") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict + + def _pprint_counter( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + + if object: + stream.write("{") + items = object.most_common() + self._format_dict_items(items, stream, indent, allowance, context, level) + stream.write("}") + + stream.write(")") + + _dispatch[_collections.Counter.__repr__] = _pprint_counter + + def _pprint_chain_map( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): + stream.write(repr(object)) + return + + stream.write(object.__class__.__name__ + "(") + self._format_items(object.maps, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map + + def _pprint_deque( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + if object.maxlen is not None: + stream.write(f"maxlen={object.maxlen}, ") + stream.write("[") + + self._format_items(object, stream, indent, allowance + 1, context, level) + stream.write("])") + + _dispatch[_collections.deque.__repr__] = _pprint_deque + + def _pprint_user_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict + + def _pprint_user_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserList.__repr__] = _pprint_user_list + + def _pprint_user_string( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserString.__repr__] = _pprint_user_string + + def _safe_repr( + self, object: Any, context: set[int], maxlevels: int | None, level: int + ) -> str: + typ = type(object) + if typ in _builtin_scalars: + return repr(object) + + r = getattr(typ, "__repr__", None) + + if issubclass(typ, dict) and r is dict.__repr__: + if not object: + return "{}" + objid = id(object) + if maxlevels and level >= maxlevels: + return "{...}" + if objid in context: + return _recursion(object) + context.add(objid) + components: list[str] = [] + append = components.append + level += 1 + for k, v in sorted(object.items(), key=_safe_tuple): + krepr = self._safe_repr(k, context, maxlevels, level) + vrepr = self._safe_repr(v, context, maxlevels, level) + append(f"{krepr}: {vrepr}") + context.remove(objid) + return "{{{}}}".format(", ".join(components)) + + if (issubclass(typ, list) and r is list.__repr__) or ( + issubclass(typ, tuple) and r is tuple.__repr__ + ): + if issubclass(typ, list): + if not object: + return "[]" + format = "[%s]" + elif len(object) == 1: + format = "(%s,)" + else: + if not object: + return "()" + format = "(%s)" + objid = id(object) + if maxlevels and level >= maxlevels: + return format % "..." + if objid in context: + return _recursion(object) + context.add(objid) + components = [] + append = components.append + level += 1 + for o in object: + orepr = self._safe_repr(o, context, maxlevels, level) + append(orepr) + context.remove(objid) + return format % ", ".join(components) + + return repr(object) + + +_builtin_scalars = frozenset( + {str, bytes, bytearray, float, complex, bool, type(None), int} +) + + +def _recursion(object: Any) -> str: + return f"" + + +def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: + current = b"" + last = len(object) // 4 * 4 + for i in range(0, len(object), 4): + part = object[i : i + 4] + candidate = current + part + if i == last: + width -= allowance + if len(repr(candidate)) > width: + if current: + yield repr(current) + current = part + else: + current = candidate + if current: + yield repr(current) diff --git a/.venv/lib/python3.11/site-packages/_pytest/_io/saferepr.py b/.venv/lib/python3.11/site-packages/_pytest/_io/saferepr.py new file mode 100644 index 00000000..cee70e33 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_io/saferepr.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import pprint +import reprlib + + +def _try_repr_or_str(obj: object) -> str: + try: + return repr(obj) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + return f'{type(obj).__name__}("{obj}")' + + +def _format_repr_exception(exc: BaseException, obj: object) -> str: + try: + exc_info = _try_repr_or_str(exc) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as inner_exc: + exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})" + return ( + f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" + ) + + +def _ellipsize(s: str, maxsize: int) -> str: + if len(s) > maxsize: + i = max(0, (maxsize - 3) // 2) + j = max(0, maxsize - 3 - i) + return s[:i] + "..." + s[len(s) - j :] + return s + + +class SafeRepr(reprlib.Repr): + """ + repr.Repr that limits the resulting size of repr() and includes + information on exceptions raised during the call. + """ + + def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: + """ + :param maxsize: + If not None, will truncate the resulting repr to that specific size, using ellipsis + somewhere in the middle to hide the extra text. + If None, will not impose any size limits on the returning repr. + """ + super().__init__() + # ``maxstring`` is used by the superclass, and needs to be an int; using a + # very large number in case maxsize is None, meaning we want to disable + # truncation. + self.maxstring = maxsize if maxsize is not None else 1_000_000_000 + self.maxsize = maxsize + self.use_ascii = use_ascii + + def repr(self, x: object) -> str: + try: + if self.use_ascii: + s = ascii(x) + else: + s = super().repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + def repr_instance(self, x: object, level: int) -> str: + try: + s = repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + +def safeformat(obj: object) -> str: + """Return a pretty printed string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info. + """ + try: + return pprint.pformat(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) + + +# Maximum size of overall repr of objects to display during assertion errors. +DEFAULT_REPR_MAX_SIZE = 240 + + +def saferepr( + obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False +) -> str: + """Return a size-limited safe repr-string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. + + This function is a wrapper around the Repr/reprlib functionality of the + stdlib. + """ + return SafeRepr(maxsize, use_ascii).repr(obj) + + +def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: + """Return an unlimited-size safe repr-string for the given object. + + As with saferepr, failing __repr__ functions of user instances + will be represented with a short exception info. + + This function is a wrapper around simple repr. + + Note: a cleaner solution would be to alter ``saferepr``this way + when maxsize=None, but that might affect some other code. + """ + try: + if use_ascii: + return ascii(obj) + return repr(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) diff --git a/.venv/lib/python3.11/site-packages/_pytest/_io/terminalwriter.py b/.venv/lib/python3.11/site-packages/_pytest/_io/terminalwriter.py new file mode 100644 index 00000000..fd808f8b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_io/terminalwriter.py @@ -0,0 +1,254 @@ +"""Helper functions for writing to terminals and files.""" + +from __future__ import annotations + +from collections.abc import Sequence +import os +import shutil +import sys +from typing import final +from typing import Literal +from typing import TextIO + +import pygments +from pygments.formatters.terminal import TerminalFormatter +from pygments.lexer import Lexer +from pygments.lexers.diff import DiffLexer +from pygments.lexers.python import PythonLexer + +from ..compat import assert_never +from .wcwidth import wcswidth + + +# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. + + +def get_terminal_width() -> int: + width, _ = shutil.get_terminal_size(fallback=(80, 24)) + + # The Windows get_terminal_size may be bogus, let's sanify a bit. + if width < 40: + width = 80 + + return width + + +def should_do_markup(file: TextIO) -> bool: + if os.environ.get("PY_COLORS") == "1": + return True + if os.environ.get("PY_COLORS") == "0": + return False + if os.environ.get("NO_COLOR"): + return False + if os.environ.get("FORCE_COLOR"): + return True + return ( + hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" + ) + + +@final +class TerminalWriter: + _esctable = dict( + black=30, + red=31, + green=32, + yellow=33, + blue=34, + purple=35, + cyan=36, + white=37, + Black=40, + Red=41, + Green=42, + Yellow=43, + Blue=44, + Purple=45, + Cyan=46, + White=47, + bold=1, + light=2, + blink=5, + invert=7, + ) + + def __init__(self, file: TextIO | None = None) -> None: + if file is None: + file = sys.stdout + if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32": + try: + import colorama + except ImportError: + pass + else: + file = colorama.AnsiToWin32(file).stream + assert file is not None + self._file = file + self.hasmarkup = should_do_markup(file) + self._current_line = "" + self._terminal_width: int | None = None + self.code_highlight = True + + @property + def fullwidth(self) -> int: + if self._terminal_width is not None: + return self._terminal_width + return get_terminal_width() + + @fullwidth.setter + def fullwidth(self, value: int) -> None: + self._terminal_width = value + + @property + def width_of_current_line(self) -> int: + """Return an estimate of the width so far in the current line.""" + return wcswidth(self._current_line) + + def markup(self, text: str, **markup: bool) -> str: + for name in markup: + if name not in self._esctable: + raise ValueError(f"unknown markup: {name!r}") + if self.hasmarkup: + esc = [self._esctable[name] for name, on in markup.items() if on] + if esc: + text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m" + return text + + def sep( + self, + sepchar: str, + title: str | None = None, + fullwidth: int | None = None, + **markup: bool, + ) -> None: + if fullwidth is None: + fullwidth = self.fullwidth + # The goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth. + if sys.platform == "win32": + # If we print in the last column on windows we are on a + # new line but there is no way to verify/neutralize this + # (we may not know the exact line width). + # So let's be defensive to avoid empty lines in the output. + fullwidth -= 1 + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) + fill = sepchar * N + line = f"{fill} {title} {fill}" + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # In some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line. + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + + self.line(line, **markup) + + def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: + if msg: + current_line = msg.rsplit("\n", 1)[-1] + if "\n" in msg: + self._current_line = current_line + else: + self._current_line += current_line + + msg = self.markup(msg, **markup) + + try: + self._file.write(msg) + except UnicodeEncodeError: + # Some environments don't support printing general Unicode + # strings, due to misconfiguration or otherwise; in that case, + # print the string escaped to ASCII. + # When the Unicode situation improves we should consider + # letting the error propagate instead of masking it (see #7475 + # for one brief attempt). + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) + + if flush: + self.flush() + + def line(self, s: str = "", **markup: bool) -> None: + self.write(s, **markup) + self.write("\n") + + def flush(self) -> None: + self._file.flush() + + def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: + """Write lines of source code possibly highlighted. + + Keeping this private for now because the API is clunky. We should discuss how + to evolve the terminal writer so we can have more precise color support, for example + being able to write part of a line in one color and the rest in another, and so on. + """ + if indents and len(indents) != len(lines): + raise ValueError( + f"indents size ({len(indents)}) should have same size as lines ({len(lines)})" + ) + if not indents: + indents = [""] * len(lines) + source = "\n".join(lines) + new_lines = self._highlight(source).splitlines() + for indent, new_line in zip(indents, new_lines): + self.line(indent + new_line) + + def _get_pygments_lexer(self, lexer: Literal["python", "diff"]) -> Lexer: + if lexer == "python": + return PythonLexer() + elif lexer == "diff": + return DiffLexer() + else: + assert_never(lexer) + + def _get_pygments_formatter(self) -> TerminalFormatter: + from _pytest.config.exceptions import UsageError + + theme = os.getenv("PYTEST_THEME") + theme_mode = os.getenv("PYTEST_THEME_MODE", "dark") + + try: + return TerminalFormatter(bg=theme_mode, style=theme) + except pygments.util.ClassNotFound as e: + raise UsageError( + f"PYTEST_THEME environment variable has an invalid value: '{theme}'. " + "Hint: See available pygments styles with `pygmentize -L styles`." + ) from e + except pygments.util.OptionError as e: + raise UsageError( + f"PYTEST_THEME_MODE environment variable has an invalid value: '{theme_mode}'. " + "The allowed values are 'dark' (default) and 'light'." + ) from e + + def _highlight( + self, source: str, lexer: Literal["diff", "python"] = "python" + ) -> str: + """Highlight the given source if we have markup support.""" + if not source or not self.hasmarkup or not self.code_highlight: + return source + + pygments_lexer = self._get_pygments_lexer(lexer) + pygments_formatter = self._get_pygments_formatter() + + highlighted: str = pygments.highlight( + source, pygments_lexer, pygments_formatter + ) + # pygments terminal formatter may add a newline when there wasn't one. + # We don't want this, remove. + if highlighted[-1] == "\n" and source[-1] != "\n": + highlighted = highlighted[:-1] + + # Some lexers will not set the initial color explicitly + # which may lead to the previous color being propagated to the + # start of the expression, so reset first. + highlighted = "\x1b[0m" + highlighted + + return highlighted diff --git a/.venv/lib/python3.11/site-packages/_pytest/_io/wcwidth.py b/.venv/lib/python3.11/site-packages/_pytest/_io/wcwidth.py new file mode 100644 index 00000000..23886ff1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_io/wcwidth.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from functools import lru_cache +import unicodedata + + +@lru_cache(100) +def wcwidth(c: str) -> int: + """Determine how many columns are needed to display a character in a terminal. + + Returns -1 if the character is not printable. + Returns 0, 1 or 2 for other characters. + """ + o = ord(c) + + # ASCII fast path. + if 0x20 <= o < 0x07F: + return 1 + + # Some Cf/Zp/Zl characters which should be zero-width. + if ( + o == 0x0000 + or 0x200B <= o <= 0x200F + or 0x2028 <= o <= 0x202E + or 0x2060 <= o <= 0x2063 + ): + return 0 + + category = unicodedata.category(c) + + # Control characters. + if category == "Cc": + return -1 + + # Combining characters with zero width. + if category in ("Me", "Mn"): + return 0 + + # Full/Wide east asian characters. + if unicodedata.east_asian_width(c) in ("F", "W"): + return 2 + + return 1 + + +def wcswidth(s: str) -> int: + """Determine how many columns are needed to display a string in a terminal. + + Returns -1 if the string contains non-printable characters. + """ + width = 0 + for c in unicodedata.normalize("NFC", s): + wc = wcwidth(c) + if wc < 0: + return -1 + width += wc + return width diff --git a/.venv/lib/python3.11/site-packages/_pytest/_py/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/_py/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/_pytest/_py/error.py b/.venv/lib/python3.11/site-packages/_pytest/_py/error.py new file mode 100644 index 00000000..dace2376 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_py/error.py @@ -0,0 +1,119 @@ +"""create errno-specific classes for IO or os calls.""" + +from __future__ import annotations + +from collections.abc import Callable +import errno +import os +import sys +from typing import TYPE_CHECKING +from typing import TypeVar + + +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + P = ParamSpec("P") + +R = TypeVar("R") + + +class Error(EnvironmentError): + def __repr__(self) -> str: + return "{}.{} {!r}: {} ".format( + self.__class__.__module__, + self.__class__.__name__, + self.__class__.__doc__, + " ".join(map(str, self.args)), + # repr(self.args) + ) + + def __str__(self) -> str: + s = "[{}]: {}".format( + self.__class__.__doc__, + " ".join(map(str, self.args)), + ) + return s + + +_winerrnomap = { + 2: errno.ENOENT, + 3: errno.ENOENT, + 17: errno.EEXIST, + 18: errno.EXDEV, + 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailable + 22: errno.ENOTDIR, + 20: errno.ENOTDIR, + 267: errno.ENOTDIR, + 5: errno.EACCES, # anything better? +} + + +class ErrorMaker: + """lazily provides Exception classes for each possible POSIX errno + (as defined per the 'errno' module). All such instances + subclass EnvironmentError. + """ + + _errno2class: dict[int, type[Error]] = {} + + def __getattr__(self, name: str) -> type[Error]: + if name[0] == "_": + raise AttributeError(name) + eno = getattr(errno, name) + cls = self._geterrnoclass(eno) + setattr(self, name, cls) + return cls + + def _geterrnoclass(self, eno: int) -> type[Error]: + try: + return self._errno2class[eno] + except KeyError: + clsname = errno.errorcode.get(eno, f"UnknownErrno{eno}") + errorcls = type( + clsname, + (Error,), + {"__module__": "py.error", "__doc__": os.strerror(eno)}, + ) + self._errno2class[eno] = errorcls + return errorcls + + def checked_call( + self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs + ) -> R: + """Call a function and raise an errno-exception if applicable.""" + __tracebackhide__ = True + try: + return func(*args, **kwargs) + except Error: + raise + except OSError as value: + if not hasattr(value, "errno"): + raise + if sys.platform == "win32": + try: + # error: Invalid index type "Optional[int]" for "dict[int, int]"; expected type "int" [index] + # OK to ignore because we catch the KeyError below. + cls = self._geterrnoclass(_winerrnomap[value.errno]) # type:ignore[index] + except KeyError: + raise value + else: + # we are not on Windows, or we got a proper OSError + if value.errno is None: + cls = type( + "UnknownErrnoNone", + (Error,), + {"__module__": "py.error", "__doc__": None}, + ) + else: + cls = self._geterrnoclass(value.errno) + + raise cls(f"{func.__name__}{args!r}") + + +_error_maker = ErrorMaker() +checked_call = _error_maker.checked_call + + +def __getattr__(attr: str) -> type[Error]: + return getattr(_error_maker, attr) # type: ignore[no-any-return] diff --git a/.venv/lib/python3.11/site-packages/_pytest/_py/path.py b/.venv/lib/python3.11/site-packages/_pytest/_py/path.py new file mode 100644 index 00000000..e353c1a9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_py/path.py @@ -0,0 +1,1475 @@ +# mypy: allow-untyped-defs +"""local path implementation.""" + +from __future__ import annotations + +import atexit +from collections.abc import Callable +from contextlib import contextmanager +import fnmatch +import importlib.util +import io +import os +from os.path import abspath +from os.path import dirname +from os.path import exists +from os.path import isabs +from os.path import isdir +from os.path import isfile +from os.path import islink +from os.path import normpath +import posixpath +from stat import S_ISDIR +from stat import S_ISLNK +from stat import S_ISREG +import sys +from typing import Any +from typing import cast +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import uuid +import warnings + +from . import error + + +# Moved from local.py. +iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") + + +class Checkers: + _depend_on_existence = "exists", "link", "dir", "file" + + def __init__(self, path): + self.path = path + + def dotfile(self): + return self.path.basename.startswith(".") + + def ext(self, arg): + if not arg.startswith("."): + arg = "." + arg + return self.path.ext == arg + + def basename(self, arg): + return self.path.basename == arg + + def basestarts(self, arg): + return self.path.basename.startswith(arg) + + def relto(self, arg): + return self.path.relto(arg) + + def fnmatch(self, arg): + return self.path.fnmatch(arg) + + def endswith(self, arg): + return str(self.path).endswith(arg) + + def _evaluate(self, kw): + from .._code.source import getrawcode + + for name, value in kw.items(): + invert = False + meth = None + try: + meth = getattr(self, name) + except AttributeError: + if name[:3] == "not": + invert = True + try: + meth = getattr(self, name[3:]) + except AttributeError: + pass + if meth is None: + raise TypeError(f"no {name!r} checker available for {self.path!r}") + try: + if getrawcode(meth).co_argcount > 1: + if (not meth(value)) ^ invert: + return False + else: + if bool(value) ^ bool(meth()) ^ invert: + return False + except (error.ENOENT, error.ENOTDIR, error.EBUSY): + # EBUSY feels not entirely correct, + # but its kind of necessary since ENOMEDIUM + # is not accessible in python + for name in self._depend_on_existence: + if name in kw: + if kw.get(name): + return False + name = "not" + name + if name in kw: + if not kw.get(name): + return False + return True + + _statcache: Stat + + def _stat(self) -> Stat: + try: + return self._statcache + except AttributeError: + try: + self._statcache = self.path.stat() + except error.ELOOP: + self._statcache = self.path.lstat() + return self._statcache + + def dir(self): + return S_ISDIR(self._stat().mode) + + def file(self): + return S_ISREG(self._stat().mode) + + def exists(self): + return self._stat() + + def link(self): + st = self.path.lstat() + return S_ISLNK(st.mode) + + +class NeverRaised(Exception): + pass + + +class Visitor: + def __init__(self, fil, rec, ignore, bf, sort): + if isinstance(fil, str): + fil = FNMatcher(fil) + if isinstance(rec, str): + self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) + elif not hasattr(rec, "__call__") and rec: + self.rec = lambda path: True + else: + self.rec = rec + self.fil = fil + self.ignore = ignore + self.breadthfirst = bf + self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x) + + def gen(self, path): + try: + entries = path.listdir() + except self.ignore: + return + rec = self.rec + dirs = self.optsort( + [p for p in entries if p.check(dir=1) and (rec is None or rec(p))] + ) + if not self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + for p in self.optsort(entries): + if self.fil is None or self.fil(p): + yield p + if self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + + +class FNMatcher: + def __init__(self, pattern): + self.pattern = pattern + + def __call__(self, path): + pattern = self.pattern + + if ( + pattern.find(path.sep) == -1 + and iswin32 + and pattern.find(posixpath.sep) != -1 + ): + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posixpath.sep, path.sep) + + if pattern.find(path.sep) == -1: + name = path.basename + else: + name = str(path) # path.strpath # XXX svn? + if not os.path.isabs(pattern): + pattern = "*" + path.sep + pattern + return fnmatch.fnmatch(name, pattern) + + +def map_as_list(func, iter): + return list(map(func, iter)) + + +class Stat: + if TYPE_CHECKING: + + @property + def size(self) -> int: ... + + @property + def mtime(self) -> float: ... + + def __getattr__(self, name: str) -> Any: + return getattr(self._osstatresult, "st_" + name) + + def __init__(self, path, osstatresult): + self.path = path + self._osstatresult = osstatresult + + @property + def owner(self): + if iswin32: + raise NotImplementedError("XXX win32") + import pwd + + entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + @property + def group(self): + """Return group name of file.""" + if iswin32: + raise NotImplementedError("XXX win32") + import grp + + entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + def isdir(self): + return S_ISDIR(self._osstatresult.st_mode) + + def isfile(self): + return S_ISREG(self._osstatresult.st_mode) + + def islink(self): + self.path.lstat() + return S_ISLNK(self._osstatresult.st_mode) + + +def getuserid(user): + import pwd + + if not isinstance(user, int): + user = pwd.getpwnam(user)[2] # type:ignore[attr-defined,unused-ignore] + return user + + +def getgroupid(group): + import grp + + if not isinstance(group, int): + group = grp.getgrnam(group)[2] # type:ignore[attr-defined,unused-ignore] + return group + + +class LocalPath: + """Object oriented interface to os.path and other local filesystem + related information. + """ + + class ImportMismatchError(ImportError): + """raised on pyimport() if there is a mismatch of __file__'s""" + + sep = os.sep + + def __init__(self, path=None, expanduser=False): + """Initialize and return a local Path instance. + + Path can be relative to the current directory. + If path is None it defaults to the current working directory. + If expanduser is True, tilde-expansion is performed. + Note that Path instances always carry an absolute path. + Note also that passing in a local path object will simply return + the exact same path object. Use new() to get a new copy. + """ + if path is None: + self.strpath = error.checked_call(os.getcwd) + else: + try: + path = os.fspath(path) + except TypeError: + raise ValueError( + "can only pass None, Path instances " + "or non-empty strings to LocalPath" + ) + if expanduser: + path = os.path.expanduser(path) + self.strpath = abspath(path) + + if sys.platform != "win32": + + def chown(self, user, group, rec=0): + """Change ownership to the given user and group. + user and group may be specified by a number or + by a name. if rec is True change ownership + recursively. + """ + uid = getuserid(user) + gid = getgroupid(group) + if rec: + for x in self.visit(rec=lambda x: x.check(link=0)): + if x.check(link=0): + error.checked_call(os.chown, str(x), uid, gid) + error.checked_call(os.chown, str(self), uid, gid) + + def readlink(self) -> str: + """Return value of a symbolic link.""" + # https://github.com/python/mypy/issues/12278 + return error.checked_call(os.readlink, self.strpath) # type: ignore[arg-type,return-value,unused-ignore] + + def mklinkto(self, oldname): + """Posix style hard link to another name.""" + error.checked_call(os.link, str(oldname), str(self)) + + def mksymlinkto(self, value, absolute=1): + """Create a symbolic link with the given value (pointing to another name).""" + if absolute: + error.checked_call(os.symlink, str(value), self.strpath) + else: + base = self.common(value) + # with posix local paths '/' is always a common base + relsource = self.__class__(value).relto(base) + reldest = self.relto(base) + n = reldest.count(self.sep) + target = self.sep.join(("..",) * n + (relsource,)) + error.checked_call(os.symlink, target, self.strpath) + + def __div__(self, other): + return self.join(os.fspath(other)) + + __truediv__ = __div__ # py3k + + @property + def basename(self): + """Basename part of path.""" + return self._getbyspec("basename")[0] + + @property + def dirname(self): + """Dirname part of path.""" + return self._getbyspec("dirname")[0] + + @property + def purebasename(self): + """Pure base name of the path.""" + return self._getbyspec("purebasename")[0] + + @property + def ext(self): + """Extension of the path (including the '.').""" + return self._getbyspec("ext")[0] + + def read_binary(self): + """Read and return a bytestring from reading the path.""" + with self.open("rb") as f: + return f.read() + + def read_text(self, encoding): + """Read and return a Unicode string from reading the path.""" + with self.open("r", encoding=encoding) as f: + return f.read() + + def read(self, mode="r"): + """Read and return a bytestring from reading the path.""" + with self.open(mode) as f: + return f.read() + + def readlines(self, cr=1): + """Read and return a list of lines from the path. if cr is False, the + newline will be removed from the end of each line.""" + mode = "r" + + if not cr: + content = self.read(mode) + return content.split("\n") + else: + f = self.open(mode) + try: + return f.readlines() + finally: + f.close() + + def load(self): + """(deprecated) return object unpickled from self.read()""" + f = self.open("rb") + try: + import pickle + + return error.checked_call(pickle.load, f) + finally: + f.close() + + def move(self, target): + """Move this path to target.""" + if target.relto(self): + raise error.EINVAL(target, "cannot move path into a subdirectory of itself") + try: + self.rename(target) + except error.EXDEV: # invalid cross-device link + self.copy(target) + self.remove() + + def fnmatch(self, pattern): + """Return true if the basename/fullname matches the glob-'pattern'. + + valid pattern characters:: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + If the pattern contains a path-separator then the full path + is used for pattern matching and a '*' is prepended to the + pattern. + + if the pattern doesn't contain a path-separator the pattern + is only matched against the basename. + """ + return FNMatcher(pattern)(self) + + def relto(self, relpath): + """Return a string which is the relative part of the path + to the given 'relpath'. + """ + if not isinstance(relpath, (str, LocalPath)): + raise TypeError(f"{relpath!r}: not a string or path object") + strrelpath = str(relpath) + if strrelpath and strrelpath[-1] != self.sep: + strrelpath += self.sep + # assert strrelpath[-1] == self.sep + # assert strrelpath[-2] != self.sep + strself = self.strpath + if sys.platform == "win32" or getattr(os, "_name", None) == "nt": + if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)): + return strself[len(strrelpath) :] + elif strself.startswith(strrelpath): + return strself[len(strrelpath) :] + return "" + + def ensure_dir(self, *args): + """Ensure the path joined with args is a directory.""" + return self.ensure(*args, dir=True) + + def bestrelpath(self, dest): + """Return a string which is a relative path from self + (assumed to be a directory) to dest such that + self.join(bestrelpath) == dest and if not such + path can be determined return dest. + """ + try: + if self == dest: + return os.curdir + base = self.common(dest) + if not base: # can be the case on windows + return str(dest) + self2base = self.relto(base) + reldest = dest.relto(base) + if self2base: + n = self2base.count(self.sep) + 1 + else: + n = 0 + lst = [os.pardir] * n + if reldest: + lst.append(reldest) + target = dest.sep.join(lst) + return target + except AttributeError: + return str(dest) + + def exists(self): + return self.check() + + def isdir(self): + return self.check(dir=1) + + def isfile(self): + return self.check(file=1) + + def parts(self, reverse=False): + """Return a root-first list of all ancestor directories + plus the path itself. + """ + current = self + lst = [self] + while 1: + last = current + current = current.dirpath() + if last == current: + break + lst.append(current) + if not reverse: + lst.reverse() + return lst + + def common(self, other): + """Return the common part shared with the other path + or None if there is no common part. + """ + last = None + for x, y in zip(self.parts(), other.parts()): + if x != y: + return last + last = x + return last + + def __add__(self, other): + """Return new path object with 'other' added to the basename""" + return self.new(basename=self.basename + str(other)) + + def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False): + """Yields all paths below the current one + + fil is a filter (glob pattern or callable), if not matching the + path will not be yielded, defaulting to None (everything is + returned) + + rec is a filter (glob pattern or callable) that controls whether + a node is descended, defaulting to None + + ignore is an Exception class that is ignoredwhen calling dirlist() + on any of the paths (by default, all exceptions are reported) + + bf if True will cause a breadthfirst search instead of the + default depthfirst. Default: False + + sort if True will sort entries within each directory level. + """ + yield from Visitor(fil, rec, ignore, bf, sort).gen(self) + + def _sortlist(self, res, sort): + if sort: + if hasattr(sort, "__call__"): + warnings.warn( + DeprecationWarning( + "listdir(sort=callable) is deprecated and breaks on python3" + ), + stacklevel=3, + ) + res.sort(sort) + else: + res.sort() + + def __fspath__(self): + return self.strpath + + def __hash__(self): + s = self.strpath + if iswin32: + s = s.lower() + return hash(s) + + def __eq__(self, other): + s1 = os.fspath(self) + try: + s2 = os.fspath(other) + except TypeError: + return False + if iswin32: + s1 = s1.lower() + try: + s2 = s2.lower() + except AttributeError: + return False + return s1 == s2 + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return os.fspath(self) < os.fspath(other) + + def __gt__(self, other): + return os.fspath(self) > os.fspath(other) + + def samefile(self, other): + """Return True if 'other' references the same file as 'self'.""" + other = os.fspath(other) + if not isabs(other): + other = abspath(other) + if self == other: + return True + if not hasattr(os.path, "samefile"): + return False + return error.checked_call(os.path.samefile, self.strpath, other) + + def remove(self, rec=1, ignore_errors=False): + """Remove a file or directory (or a directory tree if rec=1). + if ignore_errors is True, errors while removing directories will + be ignored. + """ + if self.check(dir=1, link=0): + if rec: + # force remove of readonly files on windows + if iswin32: + self.chmod(0o700, rec=1) + import shutil + + error.checked_call( + shutil.rmtree, self.strpath, ignore_errors=ignore_errors + ) + else: + error.checked_call(os.rmdir, self.strpath) + else: + if iswin32: + self.chmod(0o700) + error.checked_call(os.remove, self.strpath) + + def computehash(self, hashtype="md5", chunksize=524288): + """Return hexdigest of hashvalue for this file.""" + try: + try: + import hashlib as mod + except ImportError: + if hashtype == "sha1": + hashtype = "sha" + mod = __import__(hashtype) + hash = getattr(mod, hashtype)() + except (AttributeError, ImportError): + raise ValueError(f"Don't know how to compute {hashtype!r} hash") + f = self.open("rb") + try: + while 1: + buf = f.read(chunksize) + if not buf: + return hash.hexdigest() + hash.update(buf) + finally: + f.close() + + def new(self, **kw): + """Create a modified version of this path. + the following keyword arguments modify various path parts:: + + a:/some/path/to/a/file.ext + xx drive + xxxxxxxxxxxxxxxxx dirname + xxxxxxxx basename + xxxx purebasename + xxx ext + """ + obj = object.__new__(self.__class__) + if not kw: + obj.strpath = self.strpath + return obj + drive, dirname, basename, purebasename, ext = self._getbyspec( + "drive,dirname,basename,purebasename,ext" + ) + if "basename" in kw: + if "purebasename" in kw or "ext" in kw: + raise ValueError(f"invalid specification {kw!r}") + else: + pb = kw.setdefault("purebasename", purebasename) + try: + ext = kw["ext"] + except KeyError: + pass + else: + if ext and not ext.startswith("."): + ext = "." + ext + kw["basename"] = pb + ext + + if "dirname" in kw and not kw["dirname"]: + kw["dirname"] = drive + else: + kw.setdefault("dirname", dirname) + kw.setdefault("sep", self.sep) + obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) + return obj + + def _getbyspec(self, spec: str) -> list[str]: + """See new for what 'spec' can be.""" + res = [] + parts = self.strpath.split(self.sep) + + args = filter(None, spec.split(",")) + for name in args: + if name == "drive": + res.append(parts[0]) + elif name == "dirname": + res.append(self.sep.join(parts[:-1])) + else: + basename = parts[-1] + if name == "basename": + res.append(basename) + else: + i = basename.rfind(".") + if i == -1: + purebasename, ext = basename, "" + else: + purebasename, ext = basename[:i], basename[i:] + if name == "purebasename": + res.append(purebasename) + elif name == "ext": + res.append(ext) + else: + raise ValueError(f"invalid part specification {name!r}") + return res + + def dirpath(self, *args, **kwargs): + """Return the directory path joined with any given path arguments.""" + if not kwargs: + path = object.__new__(self.__class__) + path.strpath = dirname(self.strpath) + if args: + path = path.join(*args) + return path + return self.new(basename="").join(*args, **kwargs) + + def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath: + """Return a new path by appending all 'args' as path + components. if abs=1 is used restart from root if any + of the args is an absolute path. + """ + sep = self.sep + strargs = [os.fspath(arg) for arg in args] + strpath = self.strpath + if abs: + newargs: list[str] = [] + for arg in reversed(strargs): + if isabs(arg): + strpath = arg + strargs = newargs + break + newargs.insert(0, arg) + # special case for when we have e.g. strpath == "/" + actual_sep = "" if strpath.endswith(sep) else sep + for arg in strargs: + arg = arg.strip(sep) + if iswin32: + # allow unix style paths even on windows. + arg = arg.strip("/") + arg = arg.replace("/", sep) + strpath = strpath + actual_sep + arg + actual_sep = sep + obj = object.__new__(self.__class__) + obj.strpath = normpath(strpath) + return obj + + def open(self, mode="r", ensure=False, encoding=None): + """Return an opened file with the given mode. + + If ensure is True, create parent directories if needed. + """ + if ensure: + self.dirpath().ensure(dir=1) + if encoding: + return error.checked_call( + io.open, + self.strpath, + mode, + encoding=encoding, + ) + return error.checked_call(open, self.strpath, mode) + + def _fastjoin(self, name): + child = object.__new__(self.__class__) + child.strpath = self.strpath + self.sep + name + return child + + def islink(self): + return islink(self.strpath) + + def check(self, **kw): + """Check a path for existence and properties. + + Without arguments, return True if the path exists, otherwise False. + + valid checkers:: + + file = 1 # is a file + file = 0 # is not a file (may not even exist) + dir = 1 # is a dir + link = 1 # is a link + exists = 1 # exists + + You can specify multiple checker definitions, for example:: + + path.check(file=1, link=1) # a link pointing to a file + """ + if not kw: + return exists(self.strpath) + if len(kw) == 1: + if "dir" in kw: + return not kw["dir"] ^ isdir(self.strpath) + if "file" in kw: + return not kw["file"] ^ isfile(self.strpath) + if not kw: + kw = {"exists": 1} + return Checkers(self)._evaluate(kw) + + _patternchars = set("*?[" + os.sep) + + def listdir(self, fil=None, sort=None): + """List directory contents, possibly filter by the given fil func + and possibly sorted. + """ + if fil is None and sort is None: + names = error.checked_call(os.listdir, self.strpath) + return map_as_list(self._fastjoin, names) + if isinstance(fil, str): + if not self._patternchars.intersection(fil): + child = self._fastjoin(fil) + if exists(child.strpath): + return [child] + return [] + fil = FNMatcher(fil) + names = error.checked_call(os.listdir, self.strpath) + res = [] + for name in names: + child = self._fastjoin(name) + if fil is None or fil(child): + res.append(child) + self._sortlist(res, sort) + return res + + def size(self) -> int: + """Return size of the underlying file object""" + return self.stat().size + + def mtime(self) -> float: + """Return last modification time of the path.""" + return self.stat().mtime + + def copy(self, target, mode=False, stat=False): + """Copy path to target. + + If mode is True, will copy permission from path to target. + If stat is True, copy permission, last modification + time, last access time, and flags from path to target. + """ + if self.check(file=1): + if target.check(dir=1): + target = target.join(self.basename) + assert self != target + copychunked(self, target) + if mode: + copymode(self.strpath, target.strpath) + if stat: + copystat(self, target) + else: + + def rec(p): + return p.check(link=0) + + for x in self.visit(rec=rec): + relpath = x.relto(self) + newx = target.join(relpath) + newx.dirpath().ensure(dir=1) + if x.check(link=1): + newx.mksymlinkto(x.readlink()) + continue + elif x.check(file=1): + copychunked(x, newx) + elif x.check(dir=1): + newx.ensure(dir=1) + if mode: + copymode(x.strpath, newx.strpath) + if stat: + copystat(x, newx) + + def rename(self, target): + """Rename this path to target.""" + target = os.fspath(target) + return error.checked_call(os.rename, self.strpath, target) + + def dump(self, obj, bin=1): + """Pickle object into path location""" + f = self.open("wb") + import pickle + + try: + error.checked_call(pickle.dump, obj, f, bin) + finally: + f.close() + + def mkdir(self, *args): + """Create & return the directory joined with args.""" + p = self.join(*args) + error.checked_call(os.mkdir, os.fspath(p)) + return p + + def write_binary(self, data, ensure=False): + """Write binary data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("wb") as f: + f.write(data) + + def write_text(self, data, encoding, ensure=False): + """Write text data into path using the specified encoding. + If ensure is True create missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("w", encoding=encoding) as f: + f.write(data) + + def write(self, data, mode="w", ensure=False): + """Write data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + if "b" in mode: + if not isinstance(data, bytes): + raise ValueError("can only process bytes") + else: + if not isinstance(data, str): + if not isinstance(data, bytes): + data = str(data) + else: + data = data.decode(sys.getdefaultencoding()) + f = self.open(mode) + try: + f.write(data) + finally: + f.close() + + def _ensuredirs(self): + parent = self.dirpath() + if parent == self: + return self + if parent.check(dir=0): + parent._ensuredirs() + if self.check(dir=0): + try: + self.mkdir() + except error.EEXIST: + # race condition: file/dir created by another thread/process. + # complain if it is not a dir + if self.check(dir=0): + raise + return self + + def ensure(self, *args, **kwargs): + """Ensure that an args-joined path exists (by default as + a file). if you specify a keyword argument 'dir=True' + then the path is forced to be a directory path. + """ + p = self.join(*args) + if kwargs.get("dir", 0): + return p._ensuredirs() + else: + p.dirpath()._ensuredirs() + if not p.check(file=1): + p.open("wb").close() + return p + + @overload + def stat(self, raising: Literal[True] = ...) -> Stat: ... + + @overload + def stat(self, raising: Literal[False]) -> Stat | None: ... + + def stat(self, raising: bool = True) -> Stat | None: + """Return an os.stat() tuple.""" + if raising: + return Stat(self, error.checked_call(os.stat, self.strpath)) + try: + return Stat(self, os.stat(self.strpath)) + except KeyboardInterrupt: + raise + except Exception: + return None + + def lstat(self) -> Stat: + """Return an os.lstat() tuple.""" + return Stat(self, error.checked_call(os.lstat, self.strpath)) + + def setmtime(self, mtime=None): + """Set modification time for the given path. if 'mtime' is None + (the default) then the file's mtime is set to current time. + + Note that the resolution for 'mtime' is platform dependent. + """ + if mtime is None: + return error.checked_call(os.utime, self.strpath, mtime) + try: + return error.checked_call(os.utime, self.strpath, (-1, mtime)) + except error.EINVAL: + return error.checked_call(os.utime, self.strpath, (self.atime(), mtime)) + + def chdir(self): + """Change directory to self and return old current directory""" + try: + old = self.__class__() + except error.ENOENT: + old = None + error.checked_call(os.chdir, self.strpath) + return old + + @contextmanager + def as_cwd(self): + """ + Return a context manager, which changes to the path's dir during the + managed "with" context. + On __enter__ it returns the old dir, which might be ``None``. + """ + old = self.chdir() + try: + yield old + finally: + if old is not None: + old.chdir() + + def realpath(self): + """Return a new path which contains no symbolic links.""" + return self.__class__(os.path.realpath(self.strpath)) + + def atime(self): + """Return last access time of the path.""" + return self.stat().atime + + def __repr__(self): + return f"local({self.strpath!r})" + + def __str__(self): + """Return string representation of the Path.""" + return self.strpath + + def chmod(self, mode, rec=0): + """Change permissions to the given mode. If mode is an + integer it directly encodes the os-specific modes. + if rec is True perform recursively. + """ + if not isinstance(mode, int): + raise TypeError(f"mode {mode!r} must be an integer") + if rec: + for x in self.visit(rec=rec): + error.checked_call(os.chmod, str(x), mode) + error.checked_call(os.chmod, self.strpath, mode) + + def pypkgpath(self): + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + Return None if a pkgpath cannot be determined. + """ + pkgpath = None + for parent in self.parts(reverse=True): + if parent.isdir(): + if not parent.join("__init__.py").exists(): + break + if not isimportable(parent.basename): + break + pkgpath = parent + return pkgpath + + def _ensuresyspath(self, ensuremode, path): + if ensuremode: + s = str(path) + if ensuremode == "append": + if s not in sys.path: + sys.path.append(s) + else: + if s != sys.path[0]: + sys.path.insert(0, s) + + def pyimport(self, modname=None, ensuresyspath=True): + """Return path as an imported python module. + + If modname is None, look for the containing package + and construct an according module name. + The module will be put/looked up in sys.modules. + if ensuresyspath is True then the root dir for importing + the file (taking __init__.py files into account) will + be prepended to sys.path if it isn't there already. + If ensuresyspath=="append" the root dir will be appended + if it isn't already contained in sys.path. + if ensuresyspath is False no modification of syspath happens. + + Special value of ensuresyspath=="importlib" is intended + purely for using in pytest, it is capable only of importing + separate .py files outside packages, e.g. for test suite + without any __init__.py file. It effectively allows having + same-named test modules in different places and offers + mild opt-in via this option. Note that it works only in + recent versions of python. + """ + if not self.check(): + raise error.ENOENT(self) + + if ensuresyspath == "importlib": + if modname is None: + modname = self.purebasename + spec = importlib.util.spec_from_file_location(modname, str(self)) + if spec is None or spec.loader is None: + raise ImportError(f"Can't find module {modname} at location {self!s}") + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + pkgpath = None + if modname is None: + pkgpath = self.pypkgpath() + if pkgpath is not None: + pkgroot = pkgpath.dirpath() + names = self.new(ext="").relto(pkgroot).split(self.sep) + if names[-1] == "__init__": + names.pop() + modname = ".".join(names) + else: + pkgroot = self.dirpath() + modname = self.purebasename + + self._ensuresyspath(ensuresyspath, pkgroot) + __import__(modname) + mod = sys.modules[modname] + if self.basename == "__init__.py": + return mod # we don't check anything as we might + # be in a namespace package ... too icky to check + modfile = mod.__file__ + assert modfile is not None + if modfile[-4:] in (".pyc", ".pyo"): + modfile = modfile[:-1] + elif modfile.endswith("$py.class"): + modfile = modfile[:-9] + ".py" + if modfile.endswith(os.sep + "__init__.py"): + if self.basename != "__init__.py": + modfile = modfile[:-12] + try: + issame = self.samefile(modfile) + except error.ENOENT: + issame = False + if not issame: + ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH") + if ignore != "1": + raise self.ImportMismatchError(modname, modfile, self) + return mod + else: + try: + return sys.modules[modname] + except KeyError: + # we have a custom modname, do a pseudo-import + import types + + mod = types.ModuleType(modname) + mod.__file__ = str(self) + sys.modules[modname] = mod + try: + with open(str(self), "rb") as f: + exec(f.read(), mod.__dict__) + except BaseException: + del sys.modules[modname] + raise + return mod + + def sysexec(self, *argv: os.PathLike[str], **popen_opts: Any) -> str: + """Return stdout text from executing a system child process, + where the 'self' path points to executable. + The process is directly invoked and not through a system shell. + """ + from subprocess import PIPE + from subprocess import Popen + + popen_opts.pop("stdout", None) + popen_opts.pop("stderr", None) + proc = Popen( + [str(self)] + [str(arg) for arg in argv], + **popen_opts, + stdout=PIPE, + stderr=PIPE, + ) + stdout: str | bytes + stdout, stderr = proc.communicate() + ret = proc.wait() + if isinstance(stdout, bytes): + stdout = stdout.decode(sys.getdefaultencoding()) + if ret != 0: + if isinstance(stderr, bytes): + stderr = stderr.decode(sys.getdefaultencoding()) + raise RuntimeError( + ret, + ret, + str(self), + stdout, + stderr, + ) + return stdout + + @classmethod + def sysfind(cls, name, checker=None, paths=None): + """Return a path object found by looking at the systems + underlying PATH specification. If the checker is not None + it will be invoked to filter matching paths. If a binary + cannot be found, None is returned + Note: This is probably not working on plain win32 systems + but may work on cygwin. + """ + if isabs(name): + p = local(name) + if p.check(file=1): + return p + else: + if paths is None: + if iswin32: + paths = os.environ["Path"].split(";") + if "" not in paths and "." not in paths: + paths.append(".") + try: + systemroot = os.environ["SYSTEMROOT"] + except KeyError: + pass + else: + paths = [ + path.replace("%SystemRoot%", systemroot) for path in paths + ] + else: + paths = os.environ["PATH"].split(":") + tryadd = [] + if iswin32: + tryadd += os.environ["PATHEXT"].split(os.pathsep) + tryadd.append("") + + for x in paths: + for addext in tryadd: + p = local(x).join(name, abs=True) + addext + try: + if p.check(file=1): + if checker: + if not checker(p): + continue + return p + except error.EACCES: + pass + return None + + @classmethod + def _gethomedir(cls): + try: + x = os.environ["HOME"] + except KeyError: + try: + x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + except KeyError: + return None + return cls(x) + + # """ + # special class constructors for local filesystem paths + # """ + @classmethod + def get_temproot(cls): + """Return the system's temporary directory + (where tempfiles are usually created in) + """ + import tempfile + + return local(tempfile.gettempdir()) + + @classmethod + def mkdtemp(cls, rootdir=None): + """Return a Path object pointing to a fresh new temporary directory + (which we created ourselves). + """ + import tempfile + + if rootdir is None: + rootdir = cls.get_temproot() + path = error.checked_call(tempfile.mkdtemp, dir=str(rootdir)) + return cls(path) + + @classmethod + def make_numbered_dir( + cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800 + ): # two days + """Return unique directory with a number greater than the current + maximum one. The number is assumed to start directly after prefix. + if keep is true directories with a number less than (maxnum-keep) + will be removed. If .lock files are used (lock_timeout non-zero), + algorithm is multi-process safe. + """ + if rootdir is None: + rootdir = cls.get_temproot() + + nprefix = prefix.lower() + + def parse_num(path): + """Parse the number out of a path (if it matches the prefix)""" + nbasename = path.basename.lower() + if nbasename.startswith(nprefix): + try: + return int(nbasename[len(nprefix) :]) + except ValueError: + pass + + def create_lockfile(path): + """Exclusively create lockfile. Throws when failed""" + mypid = os.getpid() + lockfile = path.join(".lock") + if hasattr(lockfile, "mksymlinkto"): + lockfile.mksymlinkto(str(mypid)) + else: + fd = error.checked_call( + os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + ) + with os.fdopen(fd, "w") as f: + f.write(str(mypid)) + return lockfile + + def atexit_remove_lockfile(lockfile): + """Ensure lockfile is removed at process exit""" + mypid = os.getpid() + + def try_remove_lockfile(): + # in a fork() situation, only the last process should + # remove the .lock, otherwise the other processes run the + # risk of seeing their temporary dir disappear. For now + # we remove the .lock in the parent only (i.e. we assume + # that the children finish before the parent). + if os.getpid() != mypid: + return + try: + lockfile.remove() + except error.Error: + pass + + atexit.register(try_remove_lockfile) + + # compute the maximum number currently in use with the prefix + lastmax = None + while True: + maxnum = -1 + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None: + maxnum = max(maxnum, num) + + # make the new directory + try: + udir = rootdir.mkdir(prefix + str(maxnum + 1)) + if lock_timeout: + lockfile = create_lockfile(udir) + atexit_remove_lockfile(lockfile) + except (error.EEXIST, error.ENOENT, error.EBUSY): + # race condition (1): another thread/process created the dir + # in the meantime - try again + # race condition (2): another thread/process spuriously acquired + # lock treating empty directory as candidate + # for removal - try again + # race condition (3): another thread/process tried to create the lock at + # the same time (happened in Python 3.3 on Windows) + # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa + if lastmax == maxnum: + raise + lastmax = maxnum + continue + break + + def get_mtime(path): + """Read file modification time""" + try: + return path.lstat().mtime + except error.Error: + pass + + garbage_prefix = prefix + "garbage-" + + def is_garbage(path): + """Check if path denotes directory scheduled for removal""" + bn = path.basename + return bn.startswith(garbage_prefix) + + # prune old directories + udir_time = get_mtime(udir) + if keep and udir_time: + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None and num <= (maxnum - keep): + try: + # try acquiring lock to remove directory as exclusive user + if lock_timeout: + create_lockfile(path) + except (error.EEXIST, error.ENOENT, error.EBUSY): + path_time = get_mtime(path) + if not path_time: + # assume directory doesn't exist now + continue + if abs(udir_time - path_time) < lock_timeout: + # assume directory with lockfile exists + # and lock timeout hasn't expired yet + continue + + # path dir locked for exclusive use + # and scheduled for removal to avoid another thread/process + # treating it as a new directory or removal candidate + garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4())) + try: + path.rename(garbage_path) + garbage_path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + if is_garbage(path): + try: + path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + + # make link... + try: + username = os.environ["USER"] # linux, et al + except KeyError: + try: + username = os.environ["USERNAME"] # windows + except KeyError: + username = "current" + + src = str(udir) + dest = src[: src.rfind("-")] + "-" + username + try: + os.unlink(dest) + except OSError: + pass + try: + os.symlink(src, dest) + except (OSError, AttributeError, NotImplementedError): + pass + + return udir + + +def copymode(src, dest): + """Copy permission from src to dst.""" + import shutil + + shutil.copymode(src, dest) + + +def copystat(src, dest): + """Copy permission, last modification time, + last access time, and flags from src to dst.""" + import shutil + + shutil.copystat(str(src), str(dest)) + + +def copychunked(src, dest): + chunksize = 524288 # half a meg of bytes + fsrc = src.open("rb") + try: + fdest = dest.open("wb") + try: + while 1: + buf = fsrc.read(chunksize) + if not buf: + break + fdest.write(buf) + finally: + fdest.close() + finally: + fsrc.close() + + +def isimportable(name): + if name and (name[0].isalpha() or name[0] == "_"): + name = name.replace("_", "") + return not name or name.isalnum() + + +local = LocalPath diff --git a/.venv/lib/python3.11/site-packages/_pytest/_version.py b/.venv/lib/python3.11/site-packages/_pytest/_version.py new file mode 100644 index 00000000..1eaa448c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '8.4.2' +__version_tuple__ = version_tuple = (8, 4, 2) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.11/site-packages/_pytest/assertion/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/assertion/__init__.py new file mode 100644 index 00000000..22f3ca8e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,208 @@ +# mypy: allow-untyped-defs +"""Support for presenting detailed information in failing assertions.""" + +from __future__ import annotations + +from collections.abc import Generator +import sys +from typing import Any +from typing import Protocol +from typing import TYPE_CHECKING + +from _pytest.assertion import rewrite +from _pytest.assertion import truncate +from _pytest.assertion import util +from _pytest.assertion.rewrite import assertstate_key +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item + + +if TYPE_CHECKING: + from _pytest.main import Session + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--assert", + action="store", + dest="assertmode", + choices=("rewrite", "plain"), + default="rewrite", + metavar="MODE", + help=( + "Control assertion debugging tools.\n" + "'plain' performs no assertion debugging.\n" + "'rewrite' (the default) rewrites assert statements in test modules" + " on import to provide assert expression information." + ), + ) + parser.addini( + "enable_assertion_pass_hook", + type="bool", + default=False, + help="Enables the pytest_assertion_pass hook. " + "Make sure to delete any previously generated pyc cache files.", + ) + + parser.addini( + "truncation_limit_lines", + default=None, + help="Set threshold of LINES after which truncation will take effect", + ) + parser.addini( + "truncation_limit_chars", + default=None, + help=("Set threshold of CHARS after which truncation will take effect"), + ) + + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_ASSERTIONS, + help=( + "Specify a verbosity level for assertions, overriding the main level. " + "Higher levels will provide more detailed explanation when an assertion fails." + ), + ) + + +def register_assert_rewrite(*names: str) -> None: + """Register one or more module names to be rewritten on import. + + This function will make sure that this module or all modules inside + the package will get their assert statements rewritten. + Thus you should make sure to call this before the module is + actually imported, usually in your __init__.py if you are a plugin + using a package. + + :param names: The module names to register. + """ + for name in names: + if not isinstance(name, str): + msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] + raise TypeError(msg.format(repr(names))) + rewrite_hook: RewriteHook + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + rewrite_hook = hook + break + else: + rewrite_hook = DummyRewriteHook() + rewrite_hook.mark_rewrite(*names) + + +class RewriteHook(Protocol): + def mark_rewrite(self, *names: str) -> None: ... + + +class DummyRewriteHook: + """A no-op import hook for when rewriting is disabled.""" + + def mark_rewrite(self, *names: str) -> None: + pass + + +class AssertionState: + """State for the assertion plugin.""" + + def __init__(self, config: Config, mode) -> None: + self.mode = mode + self.trace = config.trace.root.get("assertion") + self.hook: rewrite.AssertionRewritingHook | None = None + + +def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: + """Try to install the rewrite hook, raise SystemError if it fails.""" + config.stash[assertstate_key] = AssertionState(config, "rewrite") + config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config.stash[assertstate_key].trace("installed rewrite import hook") + + def undo() -> None: + hook = config.stash[assertstate_key].hook + if hook is not None and hook in sys.meta_path: + sys.meta_path.remove(hook) + + config.add_cleanup(undo) + return hook + + +def pytest_collection(session: Session) -> None: + # This hook is only called when test modules are collected + # so for example not in the managing process of pytest-xdist + # (which does not collect test modules). + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks. + + The rewrite module will use util._reprcompare if it exists to use custom + reporting via the pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ + ihook = item.ihook + + def callbinrepr(op, left: object, right: object) -> str | None: + """Call the pytest_assertrepr_compare hook and prepare the result. + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are truncated unless configured otherwise + (eg. if running in verbose mode). + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ + hook_result = ihook.pytest_assertrepr_compare( + config=item.config, op=op, left=left, right=right + ) + for new_expl in hook_result: + if new_expl: + new_expl = truncate.truncate_if_required(new_expl, item) + new_expl = [line.replace("\n", "\\n") for line in new_expl] + res = "\n~".join(new_expl) + if item.config.getvalue("assertmode") == "rewrite": + res = res.replace("%", "%%") + return res + return None + + saved_assert_hooks = util._reprcompare, util._assertion_pass + util._reprcompare = callbinrepr + util._config = item.config + + if ihook.pytest_assertion_pass.get_hookimpls(): + + def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: + ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl) + + util._assertion_pass = call_assertion_pass_hook + + try: + return (yield) + finally: + util._reprcompare, util._assertion_pass = saved_assert_hooks + util._config = None + + +def pytest_sessionfinish(session: Session) -> None: + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(None) + + +def pytest_assertrepr_compare( + config: Config, op: str, left: Any, right: Any +) -> list[str] | None: + return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/.venv/lib/python3.11/site-packages/_pytest/assertion/rewrite.py b/.venv/lib/python3.11/site-packages/_pytest/assertion/rewrite.py new file mode 100644 index 00000000..c4782c7c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/assertion/rewrite.py @@ -0,0 +1,1216 @@ +"""Rewrite assertion AST to produce nice error messages.""" + +from __future__ import annotations + +import ast +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence +import errno +import functools +import importlib.abc +import importlib.machinery +import importlib.util +import io +import itertools +import marshal +import os +from pathlib import Path +from pathlib import PurePath +import struct +import sys +import tokenize +import types +from typing import IO +from typing import TYPE_CHECKING + +from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE +from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr_unlimited +from _pytest._version import version +from _pytest.assertion import util +from _pytest.config import Config +from _pytest.fixtures import FixtureFunctionDefinition +from _pytest.main import Session +from _pytest.pathlib import absolutepath +from _pytest.pathlib import fnmatch_ex +from _pytest.stash import StashKey + + +# fmt: off +from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip +# fmt:on + +if TYPE_CHECKING: + from _pytest.assertion import AssertionState + + +class Sentinel: + pass + + +assertstate_key = StashKey["AssertionState"]() + +# pytest caches rewritten pycs in pycache dirs +PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" +PYC_EXT = ".py" + ((__debug__ and "c") or "o") +PYC_TAIL = "." + PYTEST_TAG + PYC_EXT + +# Special marker that denotes we have just left a scope definition +_SCOPE_END_MARKER = Sentinel() + + +class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): + """PEP302/PEP451 import hook which rewrites asserts.""" + + def __init__(self, config: Config) -> None: + self.config = config + try: + self.fnpats = config.getini("python_files") + except ValueError: + self.fnpats = ["test_*.py", "*_test.py"] + self.session: Session | None = None + self._rewritten_names: dict[str, Path] = {} + self._must_rewrite: set[str] = set() + # flag to guard against trying to rewrite a pyc file while we are already writing another pyc file, + # which might result in infinite recursion (#3506) + self._writing_pyc = False + self._basenames_to_check_rewrite = {"conftest"} + self._marked_for_rewrite_cache: dict[str, bool] = {} + self._session_paths_checked = False + + def set_session(self, session: Session | None) -> None: + self.session = session + self._session_paths_checked = False + + # Indirection so we can mock calls to find_spec originated from the hook during testing + _find_spec = importlib.machinery.PathFinder.find_spec + + def find_spec( + self, + name: str, + path: Sequence[str | bytes] | None = None, + target: types.ModuleType | None = None, + ) -> importlib.machinery.ModuleSpec | None: + if self._writing_pyc: + return None + state = self.config.stash[assertstate_key] + if self._early_rewrite_bailout(name, state): + return None + state.trace(f"find_module called for: {name}") + + # Type ignored because mypy is confused about the `self` binding here. + spec = self._find_spec(name, path) # type: ignore + + if spec is None and path is not None: + # With --import-mode=importlib, PathFinder cannot find spec without modifying `sys.path`, + # causing inability to assert rewriting (#12659). + # At this point, try using the file path to find the module spec. + for _path_str in path: + spec = importlib.util.spec_from_file_location(name, _path_str) + if spec is not None: + break + + if ( + # the import machinery could not find a file to import + spec is None + # this is a namespace package (without `__init__.py`) + # there's nothing to rewrite there + or spec.origin is None + # we can only rewrite source files + or not isinstance(spec.loader, importlib.machinery.SourceFileLoader) + # if the file doesn't exist, we can't rewrite it + or not os.path.exists(spec.origin) + ): + return None + else: + fn = spec.origin + + if not self._should_rewrite(name, fn, state): + return None + + return importlib.util.spec_from_file_location( + name, + fn, + loader=self, + submodule_search_locations=spec.submodule_search_locations, + ) + + def create_module( + self, spec: importlib.machinery.ModuleSpec + ) -> types.ModuleType | None: + return None # default behaviour is fine + + def exec_module(self, module: types.ModuleType) -> None: + assert module.__spec__ is not None + assert module.__spec__.origin is not None + fn = Path(module.__spec__.origin) + state = self.config.stash[assertstate_key] + + self._rewritten_names[module.__name__] = fn + + # The requested module looks like a test file, so rewrite it. This is + # the most magical part of the process: load the source, rewrite the + # asserts, and load the rewritten source. We also cache the rewritten + # module code in a special pyc. We must be aware of the possibility of + # concurrent pytest processes rewriting and loading pycs. To avoid + # tricky race conditions, we maintain the following invariant: The + # cached pyc is always a complete, valid pyc. Operations on it must be + # atomic. POSIX's atomic rename comes in handy. + write = not sys.dont_write_bytecode + cache_dir = get_cache_dir(fn) + if write: + ok = try_makedirs(cache_dir) + if not ok: + write = False + state.trace(f"read only directory: {cache_dir}") + + cache_name = fn.name[:-3] + PYC_TAIL + pyc = cache_dir / cache_name + # Notice that even if we're in a read-only directory, I'm going + # to check for a cached pyc. This may not be optimal... + co = _read_pyc(fn, pyc, state.trace) + if co is None: + state.trace(f"rewriting {fn!r}") + source_stat, co = _rewrite_test(fn, self.config) + if write: + self._writing_pyc = True + try: + _write_pyc(state, co, source_stat, pyc) + finally: + self._writing_pyc = False + else: + state.trace(f"found cached rewritten pyc for {fn}") + exec(co, module.__dict__) + + def _early_rewrite_bailout(self, name: str, state: AssertionState) -> bool: + """A fast way to get out of rewriting modules. + + Profiling has shown that the call to PathFinder.find_spec (inside of + the find_spec from this class) is a major slowdown, so, this method + tries to filter what we're sure won't be rewritten before getting to + it. + """ + if self.session is not None and not self._session_paths_checked: + self._session_paths_checked = True + for initial_path in self.session._initialpaths: + # Make something as c:/projects/my_project/path.py -> + # ['c:', 'projects', 'my_project', 'path.py'] + parts = str(initial_path).split(os.sep) + # add 'path' to basenames to be checked. + self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0]) + + # Note: conftest already by default in _basenames_to_check_rewrite. + parts = name.split(".") + if parts[-1] in self._basenames_to_check_rewrite: + return False + + # For matching the name it must be as if it was a filename. + path = PurePath(*parts).with_suffix(".py") + + for pat in self.fnpats: + # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based + # on the name alone because we need to match against the full path + if os.path.dirname(pat): + return False + if fnmatch_ex(pat, path): + return False + + if self._is_marked_for_rewrite(name, state): + return False + + state.trace(f"early skip of rewriting module: {name}") + return True + + def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool: + # always rewrite conftest files + if os.path.basename(fn) == "conftest.py": + state.trace(f"rewriting conftest file: {fn!r}") + return True + + if self.session is not None: + if self.session.isinitpath(absolutepath(fn)): + state.trace(f"matched test file (was specified on cmdline): {fn!r}") + return True + + # modules not passed explicitly on the command line are only + # rewritten if they match the naming convention for test files + fn_path = PurePath(fn) + for pat in self.fnpats: + if fnmatch_ex(pat, fn_path): + state.trace(f"matched test file {fn!r}") + return True + + return self._is_marked_for_rewrite(name, state) + + def _is_marked_for_rewrite(self, name: str, state: AssertionState) -> bool: + try: + return self._marked_for_rewrite_cache[name] + except KeyError: + for marked in self._must_rewrite: + if name == marked or name.startswith(marked + "."): + state.trace(f"matched marked file {name!r} (from {marked!r})") + self._marked_for_rewrite_cache[name] = True + return True + + self._marked_for_rewrite_cache[name] = False + return False + + def mark_rewrite(self, *names: str) -> None: + """Mark import names as needing to be rewritten. + + The named module or package as well as any nested modules will + be rewritten on import. + """ + already_imported = ( + set(names).intersection(sys.modules).difference(self._rewritten_names) + ) + for name in already_imported: + mod = sys.modules[name] + if not AssertionRewriter.is_rewrite_disabled( + mod.__doc__ or "" + ) and not isinstance(mod.__loader__, type(self)): + self._warn_already_imported(name) + self._must_rewrite.update(names) + self._marked_for_rewrite_cache.clear() + + def _warn_already_imported(self, name: str) -> None: + from _pytest.warning_types import PytestAssertRewriteWarning + + self.config.issue_config_time_warning( + PytestAssertRewriteWarning( + f"Module already imported so cannot be rewritten; {name}" + ), + stacklevel=5, + ) + + def get_data(self, pathname: str | bytes) -> bytes: + """Optional PEP302 get_data API.""" + with open(pathname, "rb") as f: + return f.read() + + if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12): + from importlib.resources.abc import TraversableResources + else: + from importlib.abc import TraversableResources + + def get_resource_reader(self, name: str) -> TraversableResources: + if sys.version_info < (3, 11): + from importlib.readers import FileReader + else: + from importlib.resources.readers import FileReader + + return FileReader(types.SimpleNamespace(path=self._rewritten_names[name])) + + +def _write_pyc_fp( + fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType +) -> None: + # Technically, we don't have to have the same pyc format as + # (C)Python, since these "pycs" should never be seen by builtin + # import. However, there's little reason to deviate. + fp.write(importlib.util.MAGIC_NUMBER) + # https://www.python.org/dev/peps/pep-0552/ + flags = b"\x00\x00\x00\x00" + fp.write(flags) + # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) + mtime = int(source_stat.st_mtime) & 0xFFFFFFFF + size = source_stat.st_size & 0xFFFFFFFF + # " bool: + proc_pyc = f"{pyc}.{os.getpid()}" + try: + with open(proc_pyc, "wb") as fp: + _write_pyc_fp(fp, source_stat, co) + except OSError as e: + state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") + return False + + try: + os.replace(proc_pyc, pyc) + except OSError as e: + state.trace(f"error writing pyc file at {pyc}: {e}") + # we ignore any failure to write the cache file + # there are many reasons, permission-denied, pycache dir being a + # file etc. + return False + return True + + +def _rewrite_test(fn: Path, config: Config) -> tuple[os.stat_result, types.CodeType]: + """Read and rewrite *fn* and return the code object.""" + stat = os.stat(fn) + source = fn.read_bytes() + strfn = str(fn) + tree = ast.parse(source, filename=strfn) + rewrite_asserts(tree, source, strfn, config) + co = compile(tree, strfn, "exec", dont_inherit=True) + return stat, co + + +def _read_pyc( + source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None +) -> types.CodeType | None: + """Possibly read a pytest pyc containing rewritten code. + + Return rewritten code if successful or None if not. + """ + try: + fp = open(pyc, "rb") + except OSError: + return None + with fp: + try: + stat_result = os.stat(source) + mtime = int(stat_result.st_mtime) + size = stat_result.st_size + data = fp.read(16) + except OSError as e: + trace(f"_read_pyc({source}): OSError {e}") + return None + # Check for invalid or out of date pyc file. + if len(data) != (16): + trace(f"_read_pyc({source}): invalid pyc (too short)") + return None + if data[:4] != importlib.util.MAGIC_NUMBER: + trace(f"_read_pyc({source}): invalid pyc (bad magic number)") + return None + if data[4:8] != b"\x00\x00\x00\x00": + trace(f"_read_pyc({source}): invalid pyc (unsupported flags)") + return None + mtime_data = data[8:12] + if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: + trace(f"_read_pyc({source}): out of date") + return None + size_data = data[12:16] + if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: + trace(f"_read_pyc({source}): invalid pyc (incorrect size)") + return None + try: + co = marshal.load(fp) + except Exception as e: + trace(f"_read_pyc({source}): marshal.load error {e}") + return None + if not isinstance(co, types.CodeType): + trace(f"_read_pyc({source}): not a code object") + return None + return co + + +def rewrite_asserts( + mod: ast.Module, + source: bytes, + module_path: str | None = None, + config: Config | None = None, +) -> None: + """Rewrite the assert statements in mod.""" + AssertionRewriter(module_path, config, source).run(mod) + + +def _saferepr(obj: object) -> str: + r"""Get a safe repr of an object for assertion error messages. + + The assertion formatting (util.format_explanation()) requires + newlines to be escaped since they are a special character for it. + Normally assertion.util.format_explanation() does this but for a + custom repr it is possible to contain one of the special escape + sequences, especially '\n{' and '\n}' are likely to be present in + JSON reprs. + """ + if isinstance(obj, types.MethodType): + # for bound methods, skip redundant information + return obj.__name__ + + maxsize = _get_maxsize_for_saferepr(util._config) + if not maxsize: + return saferepr_unlimited(obj).replace("\n", "\\n") + return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") + + +def _get_maxsize_for_saferepr(config: Config | None) -> int | None: + """Get `maxsize` configuration for saferepr based on the given config object.""" + if config is None: + verbosity = 0 + else: + verbosity = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + if verbosity >= 2: + return None + if verbosity >= 1: + return DEFAULT_REPR_MAX_SIZE * 10 + return DEFAULT_REPR_MAX_SIZE + + +def _format_assertmsg(obj: object) -> str: + r"""Format the custom assertion message given. + + For strings this simply replaces newlines with '\n~' so that + util.format_explanation() will preserve them instead of escaping + newlines. For other objects saferepr() is used first. + """ + # reprlib appears to have a bug which means that if a string + # contains a newline it gets escaped, however if an object has a + # .__repr__() which contains newlines it does not get escaped. + # However in either case we want to preserve the newline. + replaces = [("\n", "\n~"), ("%", "%%")] + if not isinstance(obj, str): + obj = saferepr(obj, _get_maxsize_for_saferepr(util._config)) + replaces.append(("\\n", "\n~")) + + for r1, r2 in replaces: + obj = obj.replace(r1, r2) + + return obj + + +def _should_repr_global_name(obj: object) -> bool: + if callable(obj): + # For pytest fixtures the __repr__ method provides more information than the function name. + return isinstance(obj, FixtureFunctionDefinition) + + try: + return not hasattr(obj, "__name__") + except Exception: + return True + + +def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: + explanation = "(" + ((is_or and " or ") or " and ").join(explanations) + ")" + return explanation.replace("%", "%%") + + +def _call_reprcompare( + ops: Sequence[str], + results: Sequence[bool], + expls: Sequence[str], + each_obj: Sequence[object], +) -> str: + for i, res, expl in zip(range(len(ops)), results, expls): + try: + done = not res + except Exception: + done = True + if done: + break + if util._reprcompare is not None: + custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1]) + if custom is not None: + return custom + return expl + + +def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None: + if util._assertion_pass is not None: + util._assertion_pass(lineno, orig, expl) + + +def _check_if_assertion_pass_impl() -> bool: + """Check if any plugins implement the pytest_assertion_pass hook + in order not to generate explanation unnecessarily (might be expensive).""" + return True if util._assertion_pass else False + + +UNARY_MAP = {ast.Not: "not %s", ast.Invert: "~%s", ast.USub: "-%s", ast.UAdd: "+%s"} + +BINOP_MAP = { + ast.BitOr: "|", + ast.BitXor: "^", + ast.BitAnd: "&", + ast.LShift: "<<", + ast.RShift: ">>", + ast.Add: "+", + ast.Sub: "-", + ast.Mult: "*", + ast.Div: "/", + ast.FloorDiv: "//", + ast.Mod: "%%", # escaped for string formatting + ast.Eq: "==", + ast.NotEq: "!=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.Pow: "**", + ast.Is: "is", + ast.IsNot: "is not", + ast.In: "in", + ast.NotIn: "not in", + ast.MatMult: "@", +} + + +def traverse_node(node: ast.AST) -> Iterator[ast.AST]: + """Recursively yield node and all its children in depth-first order.""" + yield node + for child in ast.iter_child_nodes(node): + yield from traverse_node(child) + + +@functools.lru_cache(maxsize=1) +def _get_assertion_exprs(src: bytes) -> dict[int, str]: + """Return a mapping from {lineno: "assertion test expression"}.""" + ret: dict[int, str] = {} + + depth = 0 + lines: list[str] = [] + assert_lineno: int | None = None + seen_lines: set[int] = set() + + def _write_and_reset() -> None: + nonlocal depth, lines, assert_lineno, seen_lines + assert assert_lineno is not None + ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\") + depth = 0 + lines = [] + assert_lineno = None + seen_lines = set() + + tokens = tokenize.tokenize(io.BytesIO(src).readline) + for tp, source, (lineno, offset), _, line in tokens: + if tp == tokenize.NAME and source == "assert": + assert_lineno = lineno + elif assert_lineno is not None: + # keep track of depth for the assert-message `,` lookup + if tp == tokenize.OP and source in "([{": + depth += 1 + elif tp == tokenize.OP and source in ")]}": + depth -= 1 + + if not lines: + lines.append(line[offset:]) + seen_lines.add(lineno) + # a non-nested comma separates the expression from the message + elif depth == 0 and tp == tokenize.OP and source == ",": + # one line assert with message + if lineno in seen_lines and len(lines) == 1: + offset_in_trimmed = offset + len(lines[-1]) - len(line) + lines[-1] = lines[-1][:offset_in_trimmed] + # multi-line assert with message + elif lineno in seen_lines: + lines[-1] = lines[-1][:offset] + # multi line assert with escaped newline before message + else: + lines.append(line[:offset]) + _write_and_reset() + elif tp in {tokenize.NEWLINE, tokenize.ENDMARKER}: + _write_and_reset() + elif lines and lineno not in seen_lines: + lines.append(line) + seen_lines.add(lineno) + + return ret + + +class AssertionRewriter(ast.NodeVisitor): + """Assertion rewriting implementation. + + The main entrypoint is to call .run() with an ast.Module instance, + this will then find all the assert statements and rewrite them to + provide intermediate values and a detailed assertion error. See + http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html + for an overview of how this works. + + The entry point here is .run() which will iterate over all the + statements in an ast.Module and for each ast.Assert statement it + finds call .visit() with it. Then .visit_Assert() takes over and + is responsible for creating new ast statements to replace the + original assert statement: it rewrites the test of an assertion + to provide intermediate values and replace it with an if statement + which raises an assertion error with a detailed explanation in + case the expression is false and calls pytest_assertion_pass hook + if expression is true. + + For this .visit_Assert() uses the visitor pattern to visit all the + AST nodes of the ast.Assert.test field, each visit call returning + an AST node and the corresponding explanation string. During this + state is kept in several instance attributes: + + :statements: All the AST statements which will replace the assert + statement. + + :variables: This is populated by .variable() with each variable + used by the statements so that they can all be set to None at + the end of the statements. + + :variable_counter: Counter to create new unique variables needed + by statements. Variables are created using .variable() and + have the form of "@py_assert0". + + :expl_stmts: The AST statements which will be executed to get + data from the assertion. This is the code which will construct + the detailed assertion message that is used in the AssertionError + or for the pytest_assertion_pass hook. + + :explanation_specifiers: A dict filled by .explanation_param() + with %-formatting placeholders and their corresponding + expressions to use in the building of an assertion message. + This is used by .pop_format_context() to build a message. + + :stack: A stack of the explanation_specifiers dicts maintained by + .push_format_context() and .pop_format_context() which allows + to build another %-formatted string while already building one. + + :scope: A tuple containing the current scope used for variables_overwrite. + + :variables_overwrite: A dict filled with references to variables + that change value within an assert. This happens when a variable is + reassigned with the walrus operator + + This state, except the variables_overwrite, is reset on every new assert + statement visited and used by the other visitors. + """ + + def __init__( + self, module_path: str | None, config: Config | None, source: bytes + ) -> None: + super().__init__() + self.module_path = module_path + self.config = config + if config is not None: + self.enable_assertion_pass_hook = config.getini( + "enable_assertion_pass_hook" + ) + else: + self.enable_assertion_pass_hook = False + self.source = source + self.scope: tuple[ast.AST, ...] = () + self.variables_overwrite: defaultdict[tuple[ast.AST, ...], dict[str, str]] = ( + defaultdict(dict) + ) + + def run(self, mod: ast.Module) -> None: + """Find all assert statements in *mod* and rewrite them.""" + if not mod.body: + # Nothing to do. + return + + # We'll insert some special imports at the top of the module, but after any + # docstrings and __future__ imports, so first figure out where that is. + doc = getattr(mod, "docstring", None) + expect_docstring = doc is None + if doc is not None and self.is_rewrite_disabled(doc): + return + pos = 0 + item = None + for item in mod.body: + if ( + expect_docstring + and isinstance(item, ast.Expr) + and isinstance(item.value, ast.Constant) + and isinstance(item.value.value, str) + ): + doc = item.value.value + if self.is_rewrite_disabled(doc): + return + expect_docstring = False + elif ( + isinstance(item, ast.ImportFrom) + and item.level == 0 + and item.module == "__future__" + ): + pass + else: + break + pos += 1 + # Special case: for a decorated function, set the lineno to that of the + # first decorator, not the `def`. Issue #4984. + if isinstance(item, ast.FunctionDef) and item.decorator_list: + lineno = item.decorator_list[0].lineno + else: + lineno = item.lineno + # Now actually insert the special imports. + if sys.version_info >= (3, 10): + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, + ), + ] + else: + aliases = [ + ast.alias("builtins", "@py_builtins"), + ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), + ] + imports = [ + ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases + ] + mod.body[pos:pos] = imports + + # Collect asserts. + self.scope = (mod,) + nodes: list[ast.AST | Sentinel] = [mod] + while nodes: + node = nodes.pop() + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + self.scope = tuple((*self.scope, node)) + nodes.append(_SCOPE_END_MARKER) + if node == _SCOPE_END_MARKER: + self.scope = self.scope[:-1] + continue + assert isinstance(node, ast.AST) + for name, field in ast.iter_fields(node): + if isinstance(field, list): + new: list[ast.AST] = [] + for i, child in enumerate(field): + if isinstance(child, ast.Assert): + # Transform assert. + new.extend(self.visit(child)) + else: + new.append(child) + if isinstance(child, ast.AST): + nodes.append(child) + setattr(node, name, new) + elif ( + isinstance(field, ast.AST) + # Don't recurse into expressions as they can't contain + # asserts. + and not isinstance(field, ast.expr) + ): + nodes.append(field) + + @staticmethod + def is_rewrite_disabled(docstring: str) -> bool: + return "PYTEST_DONT_REWRITE" in docstring + + def variable(self) -> str: + """Get a new variable.""" + # Use a character invalid in python identifiers to avoid clashing. + name = "@py_assert" + str(next(self.variable_counter)) + self.variables.append(name) + return name + + def assign(self, expr: ast.expr) -> ast.Name: + """Give *expr* a name.""" + name = self.variable() + self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr)) + return ast.copy_location(ast.Name(name, ast.Load()), expr) + + def display(self, expr: ast.expr) -> ast.expr: + """Call saferepr on the expression.""" + return self.helper("_saferepr", expr) + + def helper(self, name: str, *args: ast.expr) -> ast.expr: + """Call a helper in this module.""" + py_name = ast.Name("@pytest_ar", ast.Load()) + attr = ast.Attribute(py_name, name, ast.Load()) + return ast.Call(attr, list(args), []) + + def builtin(self, name: str) -> ast.Attribute: + """Return the builtin called *name*.""" + builtin_name = ast.Name("@py_builtins", ast.Load()) + return ast.Attribute(builtin_name, name, ast.Load()) + + def explanation_param(self, expr: ast.expr) -> str: + """Return a new named %-formatting placeholder for expr. + + This creates a %-formatting placeholder for expr in the + current formatting context, e.g. ``%(py0)s``. The placeholder + and expr are placed in the current format context so that it + can be used on the next call to .pop_format_context(). + """ + specifier = "py" + str(next(self.variable_counter)) + self.explanation_specifiers[specifier] = expr + return "%(" + specifier + ")s" + + def push_format_context(self) -> None: + """Create a new formatting context. + + The format context is used for when an explanation wants to + have a variable value formatted in the assertion message. In + this case the value required can be added using + .explanation_param(). Finally .pop_format_context() is used + to format a string of %-formatted values as added by + .explanation_param(). + """ + self.explanation_specifiers: dict[str, ast.expr] = {} + self.stack.append(self.explanation_specifiers) + + def pop_format_context(self, expl_expr: ast.expr) -> ast.Name: + """Format the %-formatted string with current format context. + + The expl_expr should be an str ast.expr instance constructed from + the %-placeholders created by .explanation_param(). This will + add the required code to format said string to .expl_stmts and + return the ast.Name instance of the formatted string. + """ + current = self.stack.pop() + if self.stack: + self.explanation_specifiers = self.stack[-1] + keys: list[ast.expr | None] = [ast.Constant(key) for key in current.keys()] + format_dict = ast.Dict(keys, list(current.values())) + form = ast.BinOp(expl_expr, ast.Mod(), format_dict) + name = "@py_format" + str(next(self.variable_counter)) + if self.enable_assertion_pass_hook: + self.format_variables.append(name) + self.expl_stmts.append(ast.Assign([ast.Name(name, ast.Store())], form)) + return ast.Name(name, ast.Load()) + + def generic_visit(self, node: ast.AST) -> tuple[ast.Name, str]: + """Handle expressions we don't have custom code for.""" + assert isinstance(node, ast.expr) + res = self.assign(node) + return res, self.explanation_param(self.display(res)) + + def visit_Assert(self, assert_: ast.Assert) -> list[ast.stmt]: + """Return the AST statements to replace the ast.Assert instance. + + This rewrites the test of an assertion to provide + intermediate values and replace it with an if statement which + raises an assertion error with a detailed explanation in case + the expression is false. + """ + if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: + import warnings + + from _pytest.warning_types import PytestAssertRewriteWarning + + # TODO: This assert should not be needed. + assert self.module_path is not None + warnings.warn_explicit( + PytestAssertRewriteWarning( + "assertion is always true, perhaps remove parentheses?" + ), + category=None, + filename=self.module_path, + lineno=assert_.lineno, + ) + + self.statements: list[ast.stmt] = [] + self.variables: list[str] = [] + self.variable_counter = itertools.count() + + if self.enable_assertion_pass_hook: + self.format_variables: list[str] = [] + + self.stack: list[dict[str, ast.expr]] = [] + self.expl_stmts: list[ast.stmt] = [] + self.push_format_context() + # Rewrite assert into a bunch of statements. + top_condition, explanation = self.visit(assert_.test) + + negation = ast.UnaryOp(ast.Not(), top_condition) + + if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook + msg = self.pop_format_context(ast.Constant(explanation)) + + # Failed + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + gluestr = "\n>assert " + else: + assertmsg = ast.Constant("") + gluestr = "assert " + err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg) + err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) + err_name = ast.Name("AssertionError", ast.Load()) + fmt = self.helper("_format_explanation", err_msg) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + statements_fail = [] + statements_fail.extend(self.expl_stmts) + statements_fail.append(raise_) + + # Passed + fmt_pass = self.helper("_format_explanation", msg) + orig = _get_assertion_exprs(self.source)[assert_.lineno] + hook_call_pass = ast.Expr( + self.helper( + "_call_assertion_pass", + ast.Constant(assert_.lineno), + ast.Constant(orig), + fmt_pass, + ) + ) + # If any hooks implement assert_pass hook + hook_impl_test = ast.If( + self.helper("_check_if_assertion_pass_impl"), + [*self.expl_stmts, hook_call_pass], + [], + ) + statements_pass: list[ast.stmt] = [hook_impl_test] + + # Test for assertion condition + main_test = ast.If(negation, statements_fail, statements_pass) + self.statements.append(main_test) + if self.format_variables: + variables: list[ast.expr] = [ + ast.Name(name, ast.Store()) for name in self.format_variables + ] + clear_format = ast.Assign(variables, ast.Constant(None)) + self.statements.append(clear_format) + + else: # Original assertion rewriting + # Create failure message. + body = self.expl_stmts + self.statements.append(ast.If(negation, body, [])) + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + explanation = "\n>assert " + explanation + else: + assertmsg = ast.Constant("") + explanation = "assert " + explanation + template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation)) + msg = self.pop_format_context(template) + fmt = self.helper("_format_explanation", msg) + err_name = ast.Name("AssertionError", ast.Load()) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + + body.append(raise_) + + # Clear temporary variables by setting them to None. + if self.variables: + variables = [ast.Name(name, ast.Store()) for name in self.variables] + clear = ast.Assign(variables, ast.Constant(None)) + self.statements.append(clear) + # Fix locations (line numbers/column offsets). + for stmt in self.statements: + for node in traverse_node(stmt): + if getattr(node, "lineno", None) is None: + # apply the assertion location to all generated ast nodes without source location + # and preserve the location of existing nodes or generated nodes with an correct location. + ast.copy_location(node, assert_) + return self.statements + + def visit_NamedExpr(self, name: ast.NamedExpr) -> tuple[ast.NamedExpr, str]: + # This method handles the 'walrus operator' repr of the target + # name if it's a local variable or _should_repr_global_name() + # thinks it's acceptable. + locs = ast.Call(self.builtin("locals"), [], []) + target_id = name.target.id + inlocs = ast.Compare(ast.Constant(target_id), [ast.In()], [locs]) + dorepr = self.helper("_should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Constant(target_id)) + return name, self.explanation_param(expr) + + def visit_Name(self, name: ast.Name) -> tuple[ast.Name, str]: + # Display the repr of the name if it's a local variable or + # _should_repr_global_name() thinks it's acceptable. + locs = ast.Call(self.builtin("locals"), [], []) + inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs]) + dorepr = self.helper("_should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Constant(name.id)) + return name, self.explanation_param(expr) + + def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: + res_var = self.variable() + expl_list = self.assign(ast.List([], ast.Load())) + app = ast.Attribute(expl_list, "append", ast.Load()) + is_or = int(isinstance(boolop.op, ast.Or)) + body = save = self.statements + fail_save = self.expl_stmts + levels = len(boolop.values) - 1 + self.push_format_context() + # Process each operand, short-circuiting if needed. + for i, v in enumerate(boolop.values): + if i: + fail_inner: list[ast.stmt] = [] + # cond is set in a prior loop iteration below + self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa: F821 + self.expl_stmts = fail_inner + # Check if the left operand is a ast.NamedExpr and the value has already been visited + if ( + isinstance(v, ast.Compare) + and isinstance(v.left, ast.NamedExpr) + and v.left.target.id + in [ + ast_expr.id + for ast_expr in boolop.values[:i] + if hasattr(ast_expr, "id") + ] + ): + pytest_temp = self.variable() + self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] + v.left.target.id = pytest_temp + self.push_format_context() + res, expl = self.visit(v) + body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) + expl_format = self.pop_format_context(ast.Constant(expl)) + call = ast.Call(app, [expl_format], []) + self.expl_stmts.append(ast.Expr(call)) + if i < levels: + cond: ast.expr = res + if is_or: + cond = ast.UnaryOp(ast.Not(), cond) + inner: list[ast.stmt] = [] + self.statements.append(ast.If(cond, inner, [])) + self.statements = body = inner + self.statements = save + self.expl_stmts = fail_save + expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or)) + expl = self.pop_format_context(expl_template) + return ast.Name(res_var, ast.Load()), self.explanation_param(expl) + + def visit_UnaryOp(self, unary: ast.UnaryOp) -> tuple[ast.Name, str]: + pattern = UNARY_MAP[unary.op.__class__] + operand_res, operand_expl = self.visit(unary.operand) + res = self.assign(ast.copy_location(ast.UnaryOp(unary.op, operand_res), unary)) + return res, pattern % (operand_expl,) + + def visit_BinOp(self, binop: ast.BinOp) -> tuple[ast.Name, str]: + symbol = BINOP_MAP[binop.op.__class__] + left_expr, left_expl = self.visit(binop.left) + right_expr, right_expl = self.visit(binop.right) + explanation = f"({left_expl} {symbol} {right_expl})" + res = self.assign( + ast.copy_location(ast.BinOp(left_expr, binop.op, right_expr), binop) + ) + return res, explanation + + def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: + new_func, func_expl = self.visit(call.func) + arg_expls = [] + new_args = [] + new_kwargs = [] + for arg in call.args: + if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( + self.scope, {} + ): + arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] + res, expl = self.visit(arg) + arg_expls.append(expl) + new_args.append(res) + for keyword in call.keywords: + if isinstance( + keyword.value, ast.Name + ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): + keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] + res, expl = self.visit(keyword.value) + new_kwargs.append(ast.keyword(keyword.arg, res)) + if keyword.arg: + arg_expls.append(keyword.arg + "=" + expl) + else: # **args have `arg` keywords with an .arg of None + arg_expls.append("**" + expl) + + expl = "{}({})".format(func_expl, ", ".join(arg_expls)) + new_call = ast.copy_location(ast.Call(new_func, new_args, new_kwargs), call) + res = self.assign(new_call) + res_expl = self.explanation_param(self.display(res)) + outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" + return res, outer_expl + + def visit_Starred(self, starred: ast.Starred) -> tuple[ast.Starred, str]: + # A Starred node can appear in a function call. + res, expl = self.visit(starred.value) + new_starred = ast.Starred(res, starred.ctx) + return new_starred, "*" + expl + + def visit_Attribute(self, attr: ast.Attribute) -> tuple[ast.Name, str]: + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + value, value_expl = self.visit(attr.value) + res = self.assign( + ast.copy_location(ast.Attribute(value, attr.attr, ast.Load()), attr) + ) + res_expl = self.explanation_param(self.display(res)) + pat = "%s\n{%s = %s.%s\n}" + expl = pat % (res_expl, res_expl, value_expl, attr.attr) + return res, expl + + def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: + self.push_format_context() + # We first check if we have overwritten a variable in the previous assert + if isinstance( + comp.left, ast.Name + ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): + comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] + if isinstance(comp.left, ast.NamedExpr): + self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] + left_res, left_expl = self.visit(comp.left) + if isinstance(comp.left, (ast.Compare, ast.BoolOp)): + left_expl = f"({left_expl})" + res_variables = [self.variable() for i in range(len(comp.ops))] + load_names: list[ast.expr] = [ast.Name(v, ast.Load()) for v in res_variables] + store_names = [ast.Name(v, ast.Store()) for v in res_variables] + it = zip(range(len(comp.ops)), comp.ops, comp.comparators) + expls: list[ast.expr] = [] + syms: list[ast.expr] = [] + results = [left_res] + for i, op, next_operand in it: + if ( + isinstance(next_operand, ast.NamedExpr) + and isinstance(left_res, ast.Name) + and next_operand.target.id == left_res.id + ): + next_operand.target.id = self.variable() + self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] + next_res, next_expl = self.visit(next_operand) + if isinstance(next_operand, (ast.Compare, ast.BoolOp)): + next_expl = f"({next_expl})" + results.append(next_res) + sym = BINOP_MAP[op.__class__] + syms.append(ast.Constant(sym)) + expl = f"{left_expl} {sym} {next_expl}" + expls.append(ast.Constant(expl)) + res_expr = ast.copy_location(ast.Compare(left_res, [op], [next_res]), comp) + self.statements.append(ast.Assign([store_names[i]], res_expr)) + left_res, left_expl = next_res, next_expl + # Use pytest.assertion.util._reprcompare if that's available. + expl_call = self.helper( + "_call_reprcompare", + ast.Tuple(syms, ast.Load()), + ast.Tuple(load_names, ast.Load()), + ast.Tuple(expls, ast.Load()), + ast.Tuple(results, ast.Load()), + ) + if len(comp.ops) > 1: + res: ast.expr = ast.BoolOp(ast.And(), load_names) + else: + res = load_names[0] + + return res, self.explanation_param(self.pop_format_context(expl_call)) + + +def try_makedirs(cache_dir: Path) -> bool: + """Attempt to create the given directory and sub-directories exist. + + Returns True if successful or if it already exists. + """ + try: + os.makedirs(cache_dir, exist_ok=True) + except (FileNotFoundError, NotADirectoryError, FileExistsError): + # One of the path components was not a directory: + # - we're in a zip file + # - it is a file + return False + except PermissionError: + return False + except OSError as e: + # as of now, EROFS doesn't have an equivalent OSError-subclass + # + # squashfuse_ll returns ENOSYS "OSError: [Errno 38] Function not + # implemented" for a read-only error + if e.errno in {errno.EROFS, errno.ENOSYS}: + return False + raise + return True + + +def get_cache_dir(file_path: Path) -> Path: + """Return the cache directory to write .pyc files for the given .py file path.""" + if sys.pycache_prefix: + # given: + # prefix = '/tmp/pycs' + # path = '/home/user/proj/test_app.py' + # we want: + # '/tmp/pycs/home/user/proj' + return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) + else: + # classic pycache directory + return file_path.parent / "__pycache__" diff --git a/.venv/lib/python3.11/site-packages/_pytest/assertion/truncate.py b/.venv/lib/python3.11/site-packages/_pytest/assertion/truncate.py new file mode 100644 index 00000000..4854a62b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,137 @@ +"""Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +terminal lines, unless running with an assertions verbosity level of at least 2 or running on CI. +""" + +from __future__ import annotations + +from _pytest.assertion import util +from _pytest.config import Config +from _pytest.nodes import Item + + +DEFAULT_MAX_LINES = 8 +DEFAULT_MAX_CHARS = DEFAULT_MAX_LINES * 80 +USAGE_MSG = "use '-vv' to show" + + +def truncate_if_required(explanation: list[str], item: Item) -> list[str]: + """Truncate this assertion explanation if the given test item is eligible.""" + should_truncate, max_lines, max_chars = _get_truncation_parameters(item) + if should_truncate: + return _truncate_explanation( + explanation, + max_lines=max_lines, + max_chars=max_chars, + ) + return explanation + + +def _get_truncation_parameters(item: Item) -> tuple[bool, int, int]: + """Return the truncation parameters related to the given item, as (should truncate, max lines, max chars).""" + # We do not need to truncate if one of conditions is met: + # 1. Verbosity level is 2 or more; + # 2. Test is being run in CI environment; + # 3. Both truncation_limit_lines and truncation_limit_chars + # .ini parameters are set to 0 explicitly. + max_lines = item.config.getini("truncation_limit_lines") + max_lines = int(max_lines if max_lines is not None else DEFAULT_MAX_LINES) + + max_chars = item.config.getini("truncation_limit_chars") + max_chars = int(max_chars if max_chars is not None else DEFAULT_MAX_CHARS) + + verbose = item.config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + + should_truncate = verbose < 2 and not util.running_on_ci() + should_truncate = should_truncate and (max_lines > 0 or max_chars > 0) + + return should_truncate, max_lines, max_chars + + +def _truncate_explanation( + input_lines: list[str], + max_lines: int, + max_chars: int, +) -> list[str]: + """Truncate given list of strings that makes up the assertion explanation. + + Truncates to either max_lines, or max_chars - whichever the input reaches + first, taking the truncation explanation into account. The remaining lines + will be replaced by a usage message. + """ + # Check if truncation required + input_char_count = len("".join(input_lines)) + # The length of the truncation explanation depends on the number of lines + # removed but is at least 68 characters: + # The real value is + # 64 (for the base message: + # '...\n...Full output truncated (1 line hidden), use '-vv' to show")' + # ) + # + 1 (for plural) + # + int(math.log10(len(input_lines) - max_lines)) (number of hidden line, at least 1) + # + 3 for the '...' added to the truncated line + # But if there's more than 100 lines it's very likely that we're going to + # truncate, so we don't need the exact value using log10. + tolerable_max_chars = ( + max_chars + 70 # 64 + 1 (for plural) + 2 (for '99') + 3 for '...' + ) + # The truncation explanation add two lines to the output + tolerable_max_lines = max_lines + 2 + if ( + len(input_lines) <= tolerable_max_lines + and input_char_count <= tolerable_max_chars + ): + return input_lines + # Truncate first to max_lines, and then truncate to max_chars if necessary + if max_lines > 0: + truncated_explanation = input_lines[:max_lines] + else: + truncated_explanation = input_lines + truncated_char = True + # We reevaluate the need to truncate chars following removal of some lines + if len("".join(truncated_explanation)) > tolerable_max_chars and max_chars > 0: + truncated_explanation = _truncate_by_char_count( + truncated_explanation, max_chars + ) + else: + truncated_char = False + + if truncated_explanation == input_lines: + # No truncation happened, so we do not need to add any explanations + return truncated_explanation + + truncated_line_count = len(input_lines) - len(truncated_explanation) + if truncated_explanation[-1]: + # Add ellipsis and take into account part-truncated final line + truncated_explanation[-1] = truncated_explanation[-1] + "..." + if truncated_char: + # It's possible that we did not remove any char from this line + truncated_line_count += 1 + else: + # Add proper ellipsis when we were able to fit a full line exactly + truncated_explanation[-1] = "..." + return [ + *truncated_explanation, + "", + f"...Full output truncated ({truncated_line_count} line" + f"{'' if truncated_line_count == 1 else 's'} hidden), {USAGE_MSG}", + ] + + +def _truncate_by_char_count(input_lines: list[str], max_chars: int) -> list[str]: + # Find point at which input length exceeds total allowed length + iterated_char_count = 0 + for iterated_index, input_line in enumerate(input_lines): + if iterated_char_count + len(input_line) > max_chars: + break + iterated_char_count += len(input_line) + + # Create truncated explanation with modified final line + truncated_result = input_lines[:iterated_index] + final_line = input_lines[iterated_index] + if final_line: + final_line_truncate_point = max_chars - iterated_char_count + final_line = final_line[:final_line_truncate_point] + truncated_result.append(final_line) + return truncated_result diff --git a/.venv/lib/python3.11/site-packages/_pytest/assertion/util.py b/.venv/lib/python3.11/site-packages/_pytest/assertion/util.py new file mode 100644 index 00000000..c545e6cd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/assertion/util.py @@ -0,0 +1,621 @@ +# mypy: allow-untyped-defs +"""Utilities for assertion debugging.""" + +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import os +import pprint +from typing import Any +from typing import Literal +from typing import Protocol +from unicodedata import normalize + +from _pytest import outcomes +import _pytest._code +from _pytest._io.pprint import PrettyPrinter +from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr_unlimited +from _pytest.config import Config + + +# The _reprcompare attribute on the util module is used by the new assertion +# interpretation code and assertion rewriter to detect this plugin was +# loaded and in turn call the hooks defined here as part of the +# DebugInterpreter. +_reprcompare: Callable[[str, object, object], str | None] | None = None + +# Works similarly as _reprcompare attribute. Is populated with the hook call +# when pytest_runtest_setup is called. +_assertion_pass: Callable[[int, str, str], None] | None = None + +# Config object which is assigned during pytest_runtest_protocol. +_config: Config | None = None + + +class _HighlightFunc(Protocol): + def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str: + """Apply highlighting to the given source.""" + + +def dummy_highlighter(source: str, lexer: Literal["diff", "python"] = "python") -> str: + """Dummy highlighter that returns the text unprocessed. + + Needed for _notin_text, as the diff gets post-processed to only show the "+" part. + """ + return source + + +def format_explanation(explanation: str) -> str: + r"""Format an explanation. + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + lines = _split_explanation(explanation) + result = _format_lines(lines) + return "\n".join(result) + + +def _split_explanation(explanation: str) -> list[str]: + r"""Return a list of individual lines in the explanation. + + This will return a list of lines split on '\n{', '\n}' and '\n~'. + Any other newlines will be escaped and appear in the line as the + literal '\n' characters. + """ + raw_lines = (explanation or "").split("\n") + lines = [raw_lines[0]] + for values in raw_lines[1:]: + if values and values[0] in ["{", "}", "~", ">"]: + lines.append(values) + else: + lines[-1] += "\\n" + values + return lines + + +def _format_lines(lines: Sequence[str]) -> list[str]: + """Format the individual lines. + + This will replace the '{', '}' and '~' characters of our mini formatting + language with the proper 'where ...', 'and ...' and ' + ...' text, taking + care of indentation along the way. + + Return a list of formatted lines. + """ + result = list(lines[:1]) + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith("{"): + if stackcnt[-1]: + s = "and " + else: + s = "where " + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) + elif line.startswith("}"): + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line[0] in ["~", ">"] + stack[-1] += 1 + indent = len(stack) if line.startswith("~") else len(stack) - 1 + result.append(" " * indent + line[1:]) + assert len(stack) == 1 + return result + + +def issequence(x: Any) -> bool: + return isinstance(x, collections.abc.Sequence) and not isinstance(x, str) + + +def istext(x: Any) -> bool: + return isinstance(x, str) + + +def isdict(x: Any) -> bool: + return isinstance(x, dict) + + +def isset(x: Any) -> bool: + return isinstance(x, (set, frozenset)) + + +def isnamedtuple(obj: Any) -> bool: + return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None + + +def isdatacls(obj: Any) -> bool: + return getattr(obj, "__dataclass_fields__", None) is not None + + +def isattrs(obj: Any) -> bool: + return getattr(obj, "__attrs_attrs__", None) is not None + + +def isiterable(obj: Any) -> bool: + try: + iter(obj) + return not istext(obj) + except Exception: + return False + + +def has_default_eq( + obj: object, +) -> bool: + """Check if an instance of an object contains the default eq + + First, we check if the object's __eq__ attribute has __code__, + if so, we check the equally of the method code filename (__code__.co_filename) + to the default one generated by the dataclass and attr module + for dataclasses the default co_filename is , for attrs class, the __eq__ should contain "attrs eq generated" + """ + # inspired from https://github.com/willmcgugan/rich/blob/07d51ffc1aee6f16bd2e5a25b4e82850fb9ed778/rich/pretty.py#L68 + if hasattr(obj.__eq__, "__code__") and hasattr(obj.__eq__.__code__, "co_filename"): + code_filename = obj.__eq__.__code__.co_filename + + if isattrs(obj): + return "attrs generated " in code_filename + + return code_filename == "" # data class + return True + + +def assertrepr_compare( + config, op: str, left: Any, right: Any, use_ascii: bool = False +) -> list[str] | None: + """Return specialised explanations for some operators/operands.""" + verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + + # Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier. + # See issue #3246. + use_ascii = ( + isinstance(left, str) + and isinstance(right, str) + and normalize("NFD", left) == normalize("NFD", right) + ) + + if verbose > 1: + left_repr = saferepr_unlimited(left, use_ascii=use_ascii) + right_repr = saferepr_unlimited(right, use_ascii=use_ascii) + else: + # XXX: "15 chars indentation" is wrong + # ("E AssertionError: assert "); should use term width. + maxsize = ( + 80 - 15 - len(op) - 2 + ) // 2 # 15 chars indentation, 1 space around op + + left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii) + right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii) + + summary = f"{left_repr} {op} {right_repr}" + highlighter = config.get_terminal_writer()._highlight + + explanation = None + try: + if op == "==": + explanation = _compare_eq_any(left, right, highlighter, verbose) + elif op == "not in": + if istext(left) and istext(right): + explanation = _notin_text(left, right, verbose) + elif op == "!=": + if isset(left) and isset(right): + explanation = ["Both sets are equal"] + elif op == ">=": + if isset(left) and isset(right): + explanation = _compare_gte_set(left, right, highlighter, verbose) + elif op == "<=": + if isset(left) and isset(right): + explanation = _compare_lte_set(left, right, highlighter, verbose) + elif op == ">": + if isset(left) and isset(right): + explanation = _compare_gt_set(left, right, highlighter, verbose) + elif op == "<": + if isset(left) and isset(right): + explanation = _compare_lt_set(left, right, highlighter, verbose) + + except outcomes.Exit: + raise + except Exception: + repr_crash = _pytest._code.ExceptionInfo.from_current()._getreprcrash() + explanation = [ + f"(pytest_assertion plugin: representation of details failed: {repr_crash}.", + " Probably an object has a faulty __repr__.)", + ] + + if not explanation: + return None + + if explanation[0] != "": + explanation = ["", *explanation] + return [summary, *explanation] + + +def _compare_eq_any( + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0 +) -> list[str]: + explanation = [] + if istext(left) and istext(right): + explanation = _diff_text(left, right, highlighter, verbose) + else: + from _pytest.python_api import ApproxBase + + if isinstance(left, ApproxBase) or isinstance(right, ApproxBase): + # Although the common order should be obtained == expected, this ensures both ways + approx_side = left if isinstance(left, ApproxBase) else right + other_side = right if isinstance(left, ApproxBase) else left + + explanation = approx_side._repr_compare(other_side) + elif type(left) is type(right) and ( + isdatacls(left) or isattrs(left) or isnamedtuple(left) + ): + # Note: unlike dataclasses/attrs, namedtuples compare only the + # field values, not the type or field names. But this branch + # intentionally only handles the same-type case, which was often + # used in older code bases before dataclasses/attrs were available. + explanation = _compare_eq_cls(left, right, highlighter, verbose) + elif issequence(left) and issequence(right): + explanation = _compare_eq_sequence(left, right, highlighter, verbose) + elif isset(left) and isset(right): + explanation = _compare_eq_set(left, right, highlighter, verbose) + elif isdict(left) and isdict(right): + explanation = _compare_eq_dict(left, right, highlighter, verbose) + + if isiterable(left) and isiterable(right): + expl = _compare_eq_iterable(left, right, highlighter, verbose) + explanation.extend(expl) + + return explanation + + +def _diff_text( + left: str, right: str, highlighter: _HighlightFunc, verbose: int = 0 +) -> list[str]: + """Return the explanation for the diff between text. + + Unless --verbose is used this will skip leading and trailing + characters which are identical to keep the diff minimal. + """ + from difflib import ndiff + + explanation: list[str] = [] + + if verbose < 1: + i = 0 # just in case left or right has zero length + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + break + if i > 42: + i -= 10 # Provide some context + explanation = [ + f"Skipping {i} identical leading characters in diff, use -v to show" + ] + left = left[i:] + right = right[i:] + if len(left) == len(right): + for i in range(len(left)): + if left[-i] != right[-i]: + break + if i > 42: + i -= 10 # Provide some context + explanation += [ + f"Skipping {i} identical trailing " + "characters in diff, use -v to show" + ] + left = left[:-i] + right = right[:-i] + keepends = True + if left.isspace() or right.isspace(): + left = repr(str(left)) + right = repr(str(right)) + explanation += ["Strings contain only whitespace, escaping them using repr()"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + highlighter( + "\n".join( + line.strip("\n") + for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) + ), + lexer="diff", + ).splitlines() + ) + return explanation + + +def _compare_eq_iterable( + left: Iterable[Any], + right: Iterable[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + if verbose <= 0 and not running_on_ci(): + return ["Use -v to get more diff"] + # dynamic import to speedup pytest + import difflib + + left_formatting = PrettyPrinter().pformat(left).splitlines() + right_formatting = PrettyPrinter().pformat(right).splitlines() + + explanation = ["", "Full diff:"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + highlighter( + "\n".join( + line.rstrip() + for line in difflib.ndiff(right_formatting, left_formatting) + ), + lexer="diff", + ).splitlines() + ) + return explanation + + +def _compare_eq_sequence( + left: Sequence[Any], + right: Sequence[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes) + explanation: list[str] = [] + len_left = len(left) + len_right = len(right) + for i in range(min(len_left, len_right)): + if left[i] != right[i]: + if comparing_bytes: + # when comparing bytes, we want to see their ascii representation + # instead of their numeric values (#5260) + # using a slice gives us the ascii representation: + # >>> s = b'foo' + # >>> s[0] + # 102 + # >>> s[0:1] + # b'f' + left_value = left[i : i + 1] + right_value = right[i : i + 1] + else: + left_value = left[i] + right_value = right[i] + + explanation.append( + f"At index {i} diff:" + f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}" + ) + break + + if comparing_bytes: + # when comparing bytes, it doesn't help to show the "sides contain one or more + # items" longer explanation, so skip it + + return explanation + + len_diff = len_left - len_right + if len_diff: + if len_diff > 0: + dir_with_more = "Left" + extra = saferepr(left[len_right]) + else: + len_diff = 0 - len_diff + dir_with_more = "Right" + extra = saferepr(right[len_left]) + + if len_diff == 1: + explanation += [ + f"{dir_with_more} contains one more item: {highlighter(extra)}" + ] + else: + explanation += [ + f"{dir_with_more} contains {len_diff} more items, first extra item: {highlighter(extra)}" + ] + return explanation + + +def _compare_eq_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = [] + explanation.extend(_set_one_sided_diff("left", left, right, highlighter)) + explanation.extend(_set_one_sided_diff("right", right, left, highlighter)) + return explanation + + +def _compare_gt_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = _compare_gte_set(left, right, highlighter) + if not explanation: + return ["Both sets are equal"] + return explanation + + +def _compare_lt_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = _compare_lte_set(left, right, highlighter) + if not explanation: + return ["Both sets are equal"] + return explanation + + +def _compare_gte_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + return _set_one_sided_diff("right", right, left, highlighter) + + +def _compare_lte_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + return _set_one_sided_diff("left", left, right, highlighter) + + +def _set_one_sided_diff( + posn: str, + set1: AbstractSet[Any], + set2: AbstractSet[Any], + highlighter: _HighlightFunc, +) -> list[str]: + explanation = [] + diff = set1 - set2 + if diff: + explanation.append(f"Extra items in the {posn} set:") + for item in diff: + explanation.append(highlighter(saferepr(item))) + return explanation + + +def _compare_eq_dict( + left: Mapping[Any, Any], + right: Mapping[Any, Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation: list[str] = [] + set_left = set(left) + set_right = set(right) + common = set_left.intersection(set_right) + same = {k: left[k] for k in common if left[k] == right[k]} + if same and verbose < 2: + explanation += [f"Omitting {len(same)} identical items, use -vv to show"] + elif same: + explanation += ["Common items:"] + explanation += highlighter(pprint.pformat(same)).splitlines() + diff = {k for k in common if left[k] != right[k]} + if diff: + explanation += ["Differing items:"] + for k in diff: + explanation += [ + highlighter(saferepr({k: left[k]})) + + " != " + + highlighter(saferepr({k: right[k]})) + ] + extra_left = set_left - set_right + len_extra_left = len(extra_left) + if len_extra_left: + explanation.append( + f"Left contains {len_extra_left} more item{'' if len_extra_left == 1 else 's'}:" + ) + explanation.extend( + highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines() + ) + extra_right = set_right - set_left + len_extra_right = len(extra_right) + if len_extra_right: + explanation.append( + f"Right contains {len_extra_right} more item{'' if len_extra_right == 1 else 's'}:" + ) + explanation.extend( + highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines() + ) + return explanation + + +def _compare_eq_cls( + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int +) -> list[str]: + if not has_default_eq(left): + return [] + if isdatacls(left): + import dataclasses + + all_fields = dataclasses.fields(left) + fields_to_check = [info.name for info in all_fields if info.compare] + elif isattrs(left): + all_fields = left.__attrs_attrs__ + fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] + elif isnamedtuple(left): + fields_to_check = left._fields + else: + assert False + + indent = " " + same = [] + diff = [] + for field in fields_to_check: + if getattr(left, field) == getattr(right, field): + same.append(field) + else: + diff.append(field) + + explanation = [] + if same or diff: + explanation += [""] + if same and verbose < 2: + explanation.append(f"Omitting {len(same)} identical items, use -vv to show") + elif same: + explanation += ["Matching attributes:"] + explanation += highlighter(pprint.pformat(same)).splitlines() + if diff: + explanation += ["Differing attributes:"] + explanation += highlighter(pprint.pformat(diff)).splitlines() + for field in diff: + field_left = getattr(left, field) + field_right = getattr(right, field) + explanation += [ + "", + f"Drill down into differing attribute {field}:", + f"{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}", + ] + explanation += [ + indent + line + for line in _compare_eq_any( + field_left, field_right, highlighter, verbose + ) + ] + return explanation + + +def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: + index = text.find(term) + head = text[:index] + tail = text[index + len(term) :] + correct_text = head + tail + diff = _diff_text(text, correct_text, dummy_highlighter, verbose) + newdiff = [f"{saferepr(term, maxsize=42)} is contained here:"] + for line in diff: + if line.startswith("Skipping"): + continue + if line.startswith("- "): + continue + if line.startswith("+ "): + newdiff.append(" " + line[2:]) + else: + newdiff.append(line) + return newdiff + + +def running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + env_vars = ["CI", "BUILD_NUMBER"] + return any(var in os.environ for var in env_vars) diff --git a/.venv/lib/python3.11/site-packages/_pytest/cacheprovider.py b/.venv/lib/python3.11/site-packages/_pytest/cacheprovider.py new file mode 100644 index 00000000..dea60109 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/cacheprovider.py @@ -0,0 +1,625 @@ +# mypy: allow-untyped-defs +"""Implementation of the cache provider.""" + +# This plugin was not named "cache" to avoid conflicts with the external +# pytest-cache version. +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Iterable +import dataclasses +import errno +import json +import os +from pathlib import Path +import tempfile +from typing import final + +from .pathlib import resolve_from_str +from .pathlib import rm_rf +from .reports import CollectReport +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.nodes import Directory +from _pytest.nodes import File +from _pytest.reports import TestReport + + +README_CONTENT = """\ +# pytest cache directory # + +This directory contains data from the pytest's cache plugin, +which provides the `--lf` and `--ff` options, as well as the `cache` fixture. + +**Do not** commit this to version control. + +See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. +""" + +CACHEDIR_TAG_CONTENT = b"""\ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# https://bford.info/cachedir/spec.html +""" + + +@final +@dataclasses.dataclass +class Cache: + """Instance of the `cache` fixture.""" + + _cachedir: Path = dataclasses.field(repr=False) + _config: Config = dataclasses.field(repr=False) + + # Sub-directory under cache-dir for directories created by `mkdir()`. + _CACHE_PREFIX_DIRS = "d" + + # Sub-directory under cache-dir for values created by `set()`. + _CACHE_PREFIX_VALUES = "v" + + def __init__( + self, cachedir: Path, config: Config, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._cachedir = cachedir + self._config = config + + @classmethod + def for_config(cls, config: Config, *, _ispytest: bool = False) -> Cache: + """Create the Cache instance for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + cachedir = cls.cache_dir_from_config(config, _ispytest=True) + if config.getoption("cacheclear") and cachedir.is_dir(): + cls.clear_cache(cachedir, _ispytest=True) + return cls(cachedir, config, _ispytest=True) + + @classmethod + def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: + """Clear the sub-directories used to hold cached directories and values. + + :meta private: + """ + check_ispytest(_ispytest) + for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): + d = cachedir / prefix + if d.is_dir(): + rm_rf(d) + + @staticmethod + def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: + """Get the path to the cache directory for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + return resolve_from_str(config.getini("cache_dir"), config.rootpath) + + def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: + """Issue a cache warning. + + :meta private: + """ + check_ispytest(_ispytest) + import warnings + + from _pytest.warning_types import PytestCacheWarning + + warnings.warn( + PytestCacheWarning(fmt.format(**args) if args else fmt), + self._config.hook, + stacklevel=3, + ) + + def _mkdir(self, path: Path) -> None: + self._ensure_cache_dir_and_supporting_files() + path.mkdir(exist_ok=True, parents=True) + + def mkdir(self, name: str) -> Path: + """Return a directory path object with the given name. + + If the directory does not yet exist, it will be created. You can use + it to manage files to e.g. store/retrieve database dumps across test + sessions. + + .. versionadded:: 7.0 + + :param name: + Must be a string not containing a ``/`` separator. + Make sure the name contains your plugin or application + identifiers to prevent clashes with other cache users. + """ + path = Path(name) + if len(path.parts) > 1: + raise ValueError("name is not allowed to contain path separators") + res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) + self._mkdir(res) + return res + + def _getvaluepath(self, key: str) -> Path: + return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) + + def get(self, key: str, default): + """Return the cached value for the given key. + + If no value was yet cached or the value cannot be read, the specified + default is returned. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param default: + The value to return in case of a cache-miss or invalid cache value. + """ + path = self._getvaluepath(key) + try: + with path.open("r", encoding="UTF-8") as f: + return json.load(f) + except (ValueError, OSError): + return default + + def set(self, key: str, value: object) -> None: + """Save value for the given key. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param value: + Must be of any combination of basic python types, + including nested types like lists of dictionaries. + """ + path = self._getvaluepath(key) + try: + self._mkdir(path.parent) + except OSError as exc: + self.warn( + f"could not create cache path {path}: {exc}", + _ispytest=True, + ) + return + data = json.dumps(value, ensure_ascii=False, indent=2) + try: + f = path.open("w", encoding="UTF-8") + except OSError as exc: + self.warn( + f"cache could not write path {path}: {exc}", + _ispytest=True, + ) + else: + with f: + f.write(data) + + def _ensure_cache_dir_and_supporting_files(self) -> None: + """Create the cache dir and its supporting files.""" + if self._cachedir.is_dir(): + return + + self._cachedir.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory( + prefix="pytest-cache-files-", + dir=self._cachedir.parent, + ) as newpath: + path = Path(newpath) + + # Reset permissions to the default, see #12308. + # Note: there's no way to get the current umask atomically, eek. + umask = os.umask(0o022) + os.umask(umask) + path.chmod(0o777 - umask) + + with open(path.joinpath("README.md"), "x", encoding="UTF-8") as f: + f.write(README_CONTENT) + with open(path.joinpath(".gitignore"), "x", encoding="UTF-8") as f: + f.write("# Created by pytest automatically.\n*\n") + with open(path.joinpath("CACHEDIR.TAG"), "xb") as f: + f.write(CACHEDIR_TAG_CONTENT) + + try: + path.rename(self._cachedir) + except OSError as e: + # If 2 concurrent pytests both race to the rename, the loser + # gets "Directory not empty" from the rename. In this case, + # everything is handled so just continue (while letting the + # temporary directory be cleaned up). + # On Windows, the error is a FileExistsError which translates to EEXIST. + if e.errno not in (errno.ENOTEMPTY, errno.EEXIST): + raise + else: + # Create a directory in place of the one we just moved so that + # `TemporaryDirectory`'s cleanup doesn't complain. + # + # TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. + # See https://github.com/python/cpython/issues/74168. Note that passing + # delete=False would do the wrong thing in case of errors and isn't supported + # until python 3.12. + path.mkdir() + + +class LFPluginCollWrapper: + def __init__(self, lfplugin: LFPlugin) -> None: + self.lfplugin = lfplugin + self._collected_at_least_one_failure = False + + @hookimpl(wrapper=True) + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> Generator[None, CollectReport, CollectReport]: + res = yield + if isinstance(collector, (Session, Directory)): + # Sort any lf-paths to the beginning. + lf_paths = self.lfplugin._last_failed_paths + + # Use stable sort to prioritize last failed. + def sort_key(node: nodes.Item | nodes.Collector) -> bool: + return node.path in lf_paths + + res.result = sorted( + res.result, + key=sort_key, + reverse=True, + ) + + elif isinstance(collector, File): + if collector.path in self.lfplugin._last_failed_paths: + result = res.result + lastfailed = self.lfplugin.lastfailed + + # Only filter with known failures. + if not self._collected_at_least_one_failure: + if not any(x.nodeid in lastfailed for x in result): + return res + self.lfplugin.config.pluginmanager.register( + LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip" + ) + self._collected_at_least_one_failure = True + + session = collector.session + result[:] = [ + x + for x in result + if x.nodeid in lastfailed + # Include any passed arguments (not trivial to filter). + or session.isinitpath(x.path) + # Keep all sub-collectors. + or isinstance(x, nodes.Collector) + ] + + return res + + +class LFPluginCollSkipfiles: + def __init__(self, lfplugin: LFPlugin) -> None: + self.lfplugin = lfplugin + + @hookimpl + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> CollectReport | None: + if isinstance(collector, File): + if collector.path not in self.lfplugin._last_failed_paths: + self.lfplugin._skipped_files += 1 + + return CollectReport( + collector.nodeid, "passed", longrepr=None, result=[] + ) + return None + + +class LFPlugin: + """Plugin which implements the --lf (run last-failing) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + active_keys = "lf", "failedfirst" + self.active = any(config.getoption(key) for key in active_keys) + assert config.cache + self.lastfailed: dict[str, bool] = config.cache.get("cache/lastfailed", {}) + self._previously_failed_count: int | None = None + self._report_status: str | None = None + self._skipped_files = 0 # count skipped files during collection due to --lf + + if config.getoption("lf"): + self._last_failed_paths = self.get_last_failed_paths() + config.pluginmanager.register( + LFPluginCollWrapper(self), "lfplugin-collwrapper" + ) + + def get_last_failed_paths(self) -> set[Path]: + """Return a set with all Paths of the previously failed nodeids and + their parents.""" + rootpath = self.config.rootpath + result = set() + for nodeid in self.lastfailed: + path = rootpath / nodeid.split("::")[0] + result.add(path) + result.update(path.parents) + return {x for x in result if x.exists()} + + def pytest_report_collectionfinish(self) -> str | None: + if self.active and self.config.get_verbosity() >= 0: + return f"run-last-failure: {self._report_status}" + return None + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if (report.when == "call" and report.passed) or report.skipped: + self.lastfailed.pop(report.nodeid, None) + elif report.failed: + self.lastfailed[report.nodeid] = True + + def pytest_collectreport(self, report: CollectReport) -> None: + passed = report.outcome in ("passed", "skipped") + if passed: + if report.nodeid in self.lastfailed: + self.lastfailed.pop(report.nodeid) + self.lastfailed.update((item.nodeid, True) for item in report.result) + else: + self.lastfailed[report.nodeid] = True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, config: Config, items: list[nodes.Item] + ) -> Generator[None]: + res = yield + + if not self.active: + return res + + if self.lastfailed: + previously_failed = [] + previously_passed = [] + for item in items: + if item.nodeid in self.lastfailed: + previously_failed.append(item) + else: + previously_passed.append(item) + self._previously_failed_count = len(previously_failed) + + if not previously_failed: + # Running a subset of all tests with recorded failures + # only outside of it. + self._report_status = ( + f"{len(self.lastfailed)} known failures not in selected tests" + ) + else: + if self.config.getoption("lf"): + items[:] = previously_failed + config.hook.pytest_deselected(items=previously_passed) + else: # --failedfirst + items[:] = previously_failed + previously_passed + + noun = "failure" if self._previously_failed_count == 1 else "failures" + suffix = " first" if self.config.getoption("failedfirst") else "" + self._report_status = ( + f"rerun previous {self._previously_failed_count} {noun}{suffix}" + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += f" (skipped {self._skipped_files} {files_noun})" + else: + self._report_status = "no previously failed tests, " + if self.config.getoption("last_failed_no_failures") == "none": + self._report_status += "deselecting all items." + config.hook.pytest_deselected(items=items[:]) + items[:] = [] + else: + self._report_status += "not deselecting items." + + return res + + def pytest_sessionfinish(self, session: Session) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + assert config.cache is not None + saved_lastfailed = config.cache.get("cache/lastfailed", {}) + if saved_lastfailed != self.lastfailed: + config.cache.set("cache/lastfailed", self.lastfailed) + + +class NFPlugin: + """Plugin which implements the --nf (run new-first) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + self.active = config.option.newfirst + assert config.cache is not None + self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> Generator[None]: + res = yield + + if self.active: + new_items: dict[str, nodes.Item] = {} + other_items: dict[str, nodes.Item] = {} + for item in items: + if item.nodeid not in self.cached_nodeids: + new_items[item.nodeid] = item + else: + other_items[item.nodeid] = item + + items[:] = self._get_increasing_order( + new_items.values() + ) + self._get_increasing_order(other_items.values()) + self.cached_nodeids.update(new_items) + else: + self.cached_nodeids.update(item.nodeid for item in items) + + return res + + def _get_increasing_order(self, items: Iterable[nodes.Item]) -> list[nodes.Item]: + return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) + + def pytest_sessionfinish(self) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + if config.getoption("collectonly"): + return + + assert config.cache is not None + config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--lf", + "--last-failed", + action="store_true", + dest="lf", + help="Rerun only the tests that failed at the last run (or all if none failed)", + ) + group.addoption( + "--ff", + "--failed-first", + action="store_true", + dest="failedfirst", + help="Run all tests, but run the last failures first. " + "This may re-order tests and thus lead to " + "repeated fixture setup/teardown.", + ) + group.addoption( + "--nf", + "--new-first", + action="store_true", + dest="newfirst", + help="Run tests from new files first, then the rest of the tests " + "sorted by file mtime", + ) + group.addoption( + "--cache-show", + action="append", + nargs="?", + dest="cacheshow", + help=( + "Show cache contents, don't perform collection or tests. " + "Optional argument: glob (default: '*')." + ), + ) + group.addoption( + "--cache-clear", + action="store_true", + dest="cacheclear", + help="Remove all cache contents at start of test run", + ) + cache_dir_default = ".pytest_cache" + if "TOX_ENV_DIR" in os.environ: + cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) + parser.addini("cache_dir", default=cache_dir_default, help="Cache directory path") + group.addoption( + "--lfnf", + "--last-failed-no-failures", + action="store", + dest="last_failed_no_failures", + choices=("all", "none"), + default="all", + help="With ``--lf``, determines whether to execute tests when there " + "are no previously (known) failures or when no " + "cached ``lastfailed`` data was found. " + "``all`` (the default) runs the full test suite again. " + "``none`` just emits a message about no known failures and exits successfully.", + ) + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.cacheshow and not config.option.help: + from _pytest.main import wrap_session + + return wrap_session(config, cacheshow) + return None + + +@hookimpl(tryfirst=True) +def pytest_configure(config: Config) -> None: + config.cache = Cache.for_config(config, _ispytest=True) + config.pluginmanager.register(LFPlugin(config), "lfplugin") + config.pluginmanager.register(NFPlugin(config), "nfplugin") + + +@fixture +def cache(request: FixtureRequest) -> Cache: + """Return a cache object that can persist state between testing sessions. + + cache.get(key, default) + cache.set(key, value) + + Keys must be ``/`` separated strings, where the first part is usually the + name of your plugin or application to avoid clashes with other cache users. + + Values can be any object handled by the json stdlib module. + """ + assert request.config.cache is not None + return request.config.cache + + +def pytest_report_header(config: Config) -> str | None: + """Display cachedir with --cache-show and if non-default.""" + if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + assert config.cache is not None + cachedir = config.cache._cachedir + # TODO: evaluate generating upward relative paths + # starting with .., ../.. if sensible + + try: + displaypath = cachedir.relative_to(config.rootpath) + except ValueError: + displaypath = cachedir + return f"cachedir: {displaypath}" + return None + + +def cacheshow(config: Config, session: Session) -> int: + from pprint import pformat + + assert config.cache is not None + + tw = TerminalWriter() + tw.line("cachedir: " + str(config.cache._cachedir)) + if not config.cache._cachedir.is_dir(): + tw.line("cache is empty") + return 0 + + glob = config.option.cacheshow[0] + if glob is None: + glob = "*" + + dummy = object() + basedir = config.cache._cachedir + vdir = basedir / Cache._CACHE_PREFIX_VALUES + tw.sep("-", f"cache values for {glob!r}") + for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): + key = str(valpath.relative_to(vdir)) + val = config.cache.get(key, dummy) + if val is dummy: + tw.line(f"{key} contains unreadable content, will be ignored") + else: + tw.line(f"{key} contains:") + for line in pformat(val).splitlines(): + tw.line(" " + line) + + ddir = basedir / Cache._CACHE_PREFIX_DIRS + if ddir.is_dir(): + contents = sorted(ddir.rglob(glob)) + tw.sep("-", f"cache directories for {glob!r}") + for p in contents: + # if p.is_dir(): + # print("%s/" % p.relative_to(basedir)) + if p.is_file(): + key = str(p.relative_to(basedir)) + tw.line(f"{key} is a file of length {p.stat().st_size}") + return 0 diff --git a/.venv/lib/python3.11/site-packages/_pytest/capture.py b/.venv/lib/python3.11/site-packages/_pytest/capture.py new file mode 100644 index 00000000..6d98676b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/capture.py @@ -0,0 +1,1144 @@ +# mypy: allow-untyped-defs +"""Per-test stdout/stderr capturing mechanism.""" + +from __future__ import annotations + +import abc +import collections +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +import contextlib +import io +from io import UnsupportedOperation +import os +import sys +from tempfile import TemporaryFile +from types import TracebackType +from typing import Any +from typing import AnyStr +from typing import BinaryIO +from typing import cast +from typing import Final +from typing import final +from typing import Generic +from typing import Literal +from typing import NamedTuple +from typing import TextIO +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from typing_extensions import Self + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import SubRequest +from _pytest.nodes import Collector +from _pytest.nodes import File +from _pytest.nodes import Item +from _pytest.reports import CollectReport + + +_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--capture", + action="store", + default="fd", + metavar="method", + choices=["fd", "sys", "no", "tee-sys"], + help="Per-test capturing method: one of fd|sys|no|tee-sys", + ) + group._addoption( # private to use reserved lower-case short option + "-s", + action="store_const", + const="no", + dest="capture", + help="Shortcut for --capture=no", + ) + + +def _colorama_workaround() -> None: + """Ensure colorama is imported so that it attaches to the correct stdio + handles on Windows. + + colorama uses the terminal on import time. So if something does the + first import of colorama while I/O capture is active, colorama will + fail in various ways. + """ + if sys.platform.startswith("win32"): + try: + import colorama # noqa: F401 + except ImportError: + pass + + +def _readline_workaround() -> None: + """Ensure readline is imported early so it attaches to the correct stdio handles. + + This isn't a problem with the default GNU readline implementation, but in + some configurations, Python uses libedit instead (on macOS, and for prebuilt + binaries such as used by uv). + + In theory this is only needed if readline.backend == "libedit", but the + workaround consists of importing readline here, so we already worked around + the issue by the time we could check if we need to. + """ + try: + import readline # noqa: F401 + except ImportError: + pass + + +def _windowsconsoleio_workaround(stream: TextIO) -> None: + """Workaround for Windows Unicode console handling. + + Python 3.6 implemented Unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + :param stream: + In practice ``sys.stdout`` or ``sys.stderr``, but given + here as parameter for unittesting purposes. + + See https://github.com/pytest-dev/py/issues/103. + """ + if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): + return + + # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). + if not hasattr(stream, "buffer"): # type: ignore[unreachable,unused-ignore] + return + + raw_stdout = stream.buffer.raw if hasattr(stream.buffer, "raw") else stream.buffer + + if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined,unused-ignore] + return + + def _reopen_stdio(f, mode): + if not hasattr(stream.buffer, "raw") and mode[0] == "w": + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), + f.encoding, + f.errors, + f.newlines, + f.line_buffering, + ) + + sys.stdin = _reopen_stdio(sys.stdin, "rb") + sys.stdout = _reopen_stdio(sys.stdout, "wb") + sys.stderr = _reopen_stdio(sys.stderr, "wb") + + +@hookimpl(wrapper=True) +def pytest_load_initial_conftests(early_config: Config) -> Generator[None]: + ns = early_config.known_args_namespace + if ns.capture == "fd": + _windowsconsoleio_workaround(sys.stdout) + _colorama_workaround() + _readline_workaround() + pluginmanager = early_config.pluginmanager + capman = CaptureManager(ns.capture) + pluginmanager.register(capman, "capturemanager") + + # Make sure that capturemanager is properly reset at final shutdown. + early_config.add_cleanup(capman.stop_global_capturing) + + # Finally trigger conftest loading but while capturing (issue #93). + capman.start_global_capturing() + try: + try: + yield + finally: + capman.suspend_global_capture() + except BaseException: + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + raise + + +# IO Helpers. + + +class EncodedFile(io.TextIOWrapper): + __slots__ = () + + @property + def name(self) -> str: + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 + return repr(self.buffer) + + @property + def mode(self) -> str: + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. + assert hasattr(self.buffer, "mode") + return cast(str, self.buffer.mode.replace("b", "")) + + +class CaptureIO(io.TextIOWrapper): + def __init__(self) -> None: + super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + + def getvalue(self) -> str: + assert isinstance(self.buffer, io.BytesIO) + return self.buffer.getvalue().decode("UTF-8") + + +class TeeCaptureIO(CaptureIO): + def __init__(self, other: TextIO) -> None: + self._other = other + super().__init__() + + def write(self, s: str) -> int: + super().write(s) + return self._other.write(s) + + +class DontReadFromInput(TextIO): + @property + def encoding(self) -> str: + assert sys.__stdin__ is not None + return sys.__stdin__.encoding + + def read(self, size: int = -1) -> str: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + readline = read + + def __next__(self) -> str: + return self.readline() + + def readlines(self, hint: int | None = -1) -> list[str]: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + def __iter__(self) -> Iterator[str]: + return self + + def fileno(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + + def flush(self) -> None: + raise UnsupportedOperation("redirected stdin is pseudofile, has no flush()") + + def isatty(self) -> bool: + return False + + def close(self) -> None: + pass + + def readable(self) -> bool: + return False + + def seek(self, offset: int, whence: int = 0) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)") + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()") + + def truncate(self, size: int | None = None) -> int: + raise UnsupportedOperation("cannot truncate stdin") + + def write(self, data: str) -> int: + raise UnsupportedOperation("cannot write to stdin") + + def writelines(self, lines: Iterable[str]) -> None: + raise UnsupportedOperation("Cannot write to stdin") + + def writable(self) -> bool: + return False + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + pass + + @property + def buffer(self) -> BinaryIO: + # The str/bytes doesn't actually matter in this type, so OK to fake. + return self # type: ignore[return-value] + + +# Capture classes. + + +class CaptureBase(abc.ABC, Generic[AnyStr]): + EMPTY_BUFFER: AnyStr + + @abc.abstractmethod + def __init__(self, fd: int) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def start(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def done(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def suspend(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def resume(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def writeorg(self, data: AnyStr) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def snap(self) -> AnyStr: + raise NotImplementedError() + + +patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} + + +class NoCapture(CaptureBase[str]): + EMPTY_BUFFER = "" + + def __init__(self, fd: int) -> None: + pass + + def start(self) -> None: + pass + + def done(self) -> None: + pass + + def suspend(self) -> None: + pass + + def resume(self) -> None: + pass + + def snap(self) -> str: + return "" + + def writeorg(self, data: str) -> None: + pass + + +class SysCaptureBase(CaptureBase[AnyStr]): + def __init__( + self, fd: int, tmpfile: TextIO | None = None, *, tee: bool = False + ) -> None: + name = patchsysdict[fd] + self._old: TextIO = getattr(sys, name) + self.name = name + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) + self.tmpfile = tmpfile + self._state = "initialized" + + def repr(self, class_name: str) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + class_name, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def __repr__(self) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + self._assert_state("start", ("initialized",)) + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + def done(self) -> None: + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + setattr(sys, self.name, self._old) + del self._old + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + setattr(sys, self.name, self._old) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + +class SysCaptureBinary(SysCaptureBase[bytes]): + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: bytes) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() + + +class SysCapture(SysCaptureBase[str]): + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + assert isinstance(self.tmpfile, CaptureIO) + res = self.tmpfile.getvalue() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.write(data) + self._old.flush() + + +class FDCaptureBase(CaptureBase[AnyStr]): + def __init__(self, targetfd: int) -> None: + self.targetfd = targetfd + + try: + os.fstat(targetfd) + except OSError: + # FD capturing is conceptually simple -- create a temporary file, + # redirect the FD to it, redirect back when done. But when the + # target FD is invalid it throws a wrench into this lovely scheme. + # + # Tests themselves shouldn't care if the FD is valid, FD capturing + # should work regardless of external circumstances. So falling back + # to just sys capturing is not a good option. + # + # Further complications are the need to support suspend() and the + # possibility of FD reuse (e.g. the tmpfile getting the very same + # target FD). The following approach is robust, I believe. + self.targetfd_invalid: int | None = os.open(os.devnull, os.O_RDWR) + os.dup2(self.targetfd_invalid, targetfd) + else: + self.targetfd_invalid = None + self.targetfd_save = os.dup(targetfd) + + if targetfd == 0: + self.tmpfile = open(os.devnull, encoding="utf-8") + self.syscapture: CaptureBase[str] = SysCapture(targetfd) + else: + self.tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + newline="", + write_through=True, + ) + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, self.tmpfile) + else: + self.syscapture = NoCapture(targetfd) + + self._state = "initialized" + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.targetfd} oldfd={self.targetfd_save} " + f"_state={self._state!r} tmpfile={self.tmpfile!r}>" + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + """Start capturing on targetfd using memorized tmpfile.""" + self._assert_state("start", ("initialized",)) + os.dup2(self.tmpfile.fileno(), self.targetfd) + self.syscapture.start() + self._state = "started" + + def done(self) -> None: + """Stop capturing, restore streams, return original capture file, + seeked to position zero.""" + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + os.dup2(self.targetfd_save, self.targetfd) + os.close(self.targetfd_save) + if self.targetfd_invalid is not None: + if self.targetfd_invalid != self.targetfd: + os.close(self.targetfd) + os.close(self.targetfd_invalid) + self.syscapture.done() + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + if self._state == "suspended": + return + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + self.syscapture.resume() + os.dup2(self.tmpfile.fileno(), self.targetfd) + self._state = "started" + + +class FDCaptureBinary(FDCaptureBase[bytes]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces `bytes`. + """ + + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res # type: ignore[return-value] + + def writeorg(self, data: bytes) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + os.write(self.targetfd_save, data) + + +class FDCapture(FDCaptureBase[str]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces text. + """ + + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + # XXX use encoding of original stream + os.write(self.targetfd_save, data.encode("utf-8")) + + +# MultiCapture + + +# Generic NamedTuple only supported since Python 3.11. +if sys.version_info >= (3, 11) or TYPE_CHECKING: + + @final + class CaptureResult(NamedTuple, Generic[AnyStr]): + """The result of :method:`caplog.readouterr() `.""" + + out: AnyStr + err: AnyStr + +else: + + class CaptureResult( + collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024 + Generic[AnyStr], + ): + """The result of :method:`caplog.readouterr() `.""" + + __slots__ = () + + +class MultiCapture(Generic[AnyStr]): + _state = None + _in_suspended = False + + def __init__( + self, + in_: CaptureBase[AnyStr] | None, + out: CaptureBase[AnyStr] | None, + err: CaptureBase[AnyStr] | None, + ) -> None: + self.in_: CaptureBase[AnyStr] | None = in_ + self.out: CaptureBase[AnyStr] | None = out + self.err: CaptureBase[AnyStr] | None = err + + def __repr__(self) -> str: + return ( + f"" + ) + + def start_capturing(self) -> None: + self._state = "started" + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def pop_outerr_to_orig(self) -> tuple[AnyStr, AnyStr]: + """Pop current snapshot out/err capture and flush to orig streams.""" + out, err = self.readouterr() + if out: + assert self.out is not None + self.out.writeorg(out) + if err: + assert self.err is not None + self.err.writeorg(err) + return out, err + + def suspend_capturing(self, in_: bool = False) -> None: + self._state = "suspended" + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True + + def resume_capturing(self) -> None: + self._state = "started" + if self.out: + self.out.resume() + if self.err: + self.err.resume() + if self._in_suspended: + assert self.in_ is not None + self.in_.resume() + self._in_suspended = False + + def stop_capturing(self) -> None: + """Stop capturing and reset capturing streams.""" + if self._state == "stopped": + raise ValueError("was already stopped") + self._state = "stopped" + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + + def is_started(self) -> bool: + """Whether actively capturing -- not suspended or stopped.""" + return self._state == "started" + + def readouterr(self) -> CaptureResult[AnyStr]: + out = self.out.snap() if self.out else "" + err = self.err.snap() if self.err else "" + # TODO: This type error is real, need to fix. + return CaptureResult(out, err) # type: ignore[arg-type] + + +def _get_multicapture(method: _CaptureMethod) -> MultiCapture[str]: + if method == "fd": + return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) + elif method == "sys": + return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) + elif method == "no": + return MultiCapture(in_=None, out=None, err=None) + elif method == "tee-sys": + return MultiCapture( + in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) + ) + raise ValueError(f"unknown capturing method: {method!r}") + + +# CaptureManager and CaptureFixture + + +class CaptureManager: + """The capture plugin. + + Manages that the appropriate capture method is enabled/disabled during + collection and each test phase (setup, call, teardown). After each of + those points, the captured output is obtained and attached to the + collection/runtest report. + + There are two levels of capture: + + * global: enabled by default and can be suppressed by the ``-s`` + option. This is always enabled/disabled during collection and each test + phase. + + * fixture: when a test function or one of its fixture depend on the + ``capsys`` or ``capfd`` fixtures. In this case special handling is + needed to ensure the fixtures take precedence over the global capture. + """ + + def __init__(self, method: _CaptureMethod) -> None: + self._method: Final = method + self._global_capturing: MultiCapture[str] | None = None + self._capture_fixture: CaptureFixture[Any] | None = None + + def __repr__(self) -> str: + return ( + f"" + ) + + def is_capturing(self) -> str | bool: + if self.is_globally_capturing(): + return "global" + if self._capture_fixture: + return f"fixture {self._capture_fixture.request.fixturename}" + return False + + # Global capturing control + + def is_globally_capturing(self) -> bool: + return self._method != "no" + + def start_global_capturing(self) -> None: + assert self._global_capturing is None + self._global_capturing = _get_multicapture(self._method) + self._global_capturing.start_capturing() + + def stop_global_capturing(self) -> None: + if self._global_capturing is not None: + self._global_capturing.pop_outerr_to_orig() + self._global_capturing.stop_capturing() + self._global_capturing = None + + def resume_global_capture(self) -> None: + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() + + def suspend_global_capture(self, in_: bool = False) -> None: + if self._global_capturing is not None: + self._global_capturing.suspend_capturing(in_=in_) + + def suspend(self, in_: bool = False) -> None: + # Need to undo local capsys-et-al if it exists before disabling global capture. + self.suspend_fixture() + self.suspend_global_capture(in_) + + def resume(self) -> None: + self.resume_global_capture() + self.resume_fixture() + + def read_global_capture(self) -> CaptureResult[str]: + assert self._global_capturing is not None + return self._global_capturing.readouterr() + + # Fixture Control + + def set_fixture(self, capture_fixture: CaptureFixture[Any]) -> None: + if self._capture_fixture: + current_fixture = self._capture_fixture.request.fixturename + requested_fixture = capture_fixture.request.fixturename + capture_fixture.request.raiseerror( + f"cannot use {requested_fixture} and {current_fixture} at the same time" + ) + self._capture_fixture = capture_fixture + + def unset_fixture(self) -> None: + self._capture_fixture = None + + def activate_fixture(self) -> None: + """If the current item is using ``capsys`` or ``capfd``, activate + them so they take precedence over the global capture.""" + if self._capture_fixture: + self._capture_fixture._start() + + def deactivate_fixture(self) -> None: + """Deactivate the ``capsys`` or ``capfd`` fixture of this item, if any.""" + if self._capture_fixture: + self._capture_fixture.close() + + def suspend_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._suspend() + + def resume_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._resume() + + # Helper context managers + + @contextlib.contextmanager + def global_and_fixture_disabled(self) -> Generator[None]: + """Context manager to temporarily disable global and current fixture capturing.""" + do_fixture = self._capture_fixture and self._capture_fixture._is_started() + if do_fixture: + self.suspend_fixture() + do_global = self._global_capturing and self._global_capturing.is_started() + if do_global: + self.suspend_global_capture() + try: + yield + finally: + if do_global: + self.resume_global_capture() + if do_fixture: + self.resume_fixture() + + @contextlib.contextmanager + def item_capture(self, when: str, item: Item) -> Generator[None]: + self.resume_global_capture() + self.activate_fixture() + try: + yield + finally: + self.deactivate_fixture() + self.suspend_global_capture(in_=False) + + out, err = self.read_global_capture() + item.add_report_section(when, "stdout", out) + item.add_report_section(when, "stderr", err) + + # Hooks + + @hookimpl(wrapper=True) + def pytest_make_collect_report( + self, collector: Collector + ) -> Generator[None, CollectReport, CollectReport]: + if isinstance(collector, File): + self.resume_global_capture() + try: + rep = yield + finally: + self.suspend_global_capture() + out, err = self.read_global_capture() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + rep = yield + return rep + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: Item) -> Generator[None]: + with self.item_capture("setup", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: Item) -> Generator[None]: + with self.item_capture("call", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: Item) -> Generator[None]: + with self.item_capture("teardown", item): + return (yield) + + @hookimpl(tryfirst=True) + def pytest_keyboard_interrupt(self) -> None: + self.stop_global_capturing() + + @hookimpl(tryfirst=True) + def pytest_internalerror(self) -> None: + self.stop_global_capturing() + + +class CaptureFixture(Generic[AnyStr]): + """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, + :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" + + def __init__( + self, + captureclass: type[CaptureBase[AnyStr]], + request: SubRequest, + *, + config: dict[str, Any] | None = None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.captureclass: type[CaptureBase[AnyStr]] = captureclass + self.request = request + self._config = config if config else {} + self._capture: MultiCapture[AnyStr] | None = None + self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER + self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER + + def _start(self) -> None: + if self._capture is None: + self._capture = MultiCapture( + in_=None, + out=self.captureclass(1, **self._config), + err=self.captureclass(2, **self._config), + ) + self._capture.start_capturing() + + def close(self) -> None: + if self._capture is not None: + out, err = self._capture.pop_outerr_to_orig() + self._captured_out += out + self._captured_err += err + self._capture.stop_capturing() + self._capture = None + + def readouterr(self) -> CaptureResult[AnyStr]: + """Read and return the captured output so far, resetting the internal + buffer. + + :returns: + The captured content as a namedtuple with ``out`` and ``err`` + string attributes. + """ + captured_out, captured_err = self._captured_out, self._captured_err + if self._capture is not None: + out, err = self._capture.readouterr() + captured_out += out + captured_err += err + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + return CaptureResult(captured_out, captured_err) + + def _suspend(self) -> None: + """Suspend this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.suspend_capturing() + + def _resume(self) -> None: + """Resume this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.resume_capturing() + + def _is_started(self) -> bool: + """Whether actively capturing -- not disabled or closed.""" + if self._capture is not None: + return self._capture.is_started() + return False + + @contextlib.contextmanager + def disabled(self) -> Generator[None]: + """Temporarily disable capturing while inside the ``with`` block.""" + capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( + "capturemanager" + ) + with capmanager.global_and_fixture_disabled(): + yield + + +# The fixtures. + + +@fixture +def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capsys): + print("hello") + captured = capsys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capteesys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable simultaneous text capturing and pass-through of writes + to ``sys.stdout`` and ``sys.stderr`` as defined by ``--capture=``. + + + The captured output is made available via ``capteesys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + The output is also passed-through, allowing it to be "live-printed", + reported, or both as defined by ``--capture=``. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capteesys): + print("hello") + captured = capteesys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture( + SysCapture, request, config=dict(tee=True), _ispytest=True + ) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsysbinary.readouterr()`` + method calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``bytes`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_output(capsysbinary): + print("hello") + captured = capsysbinary.readouterr() + assert captured.out == b"hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfd): + os.system('echo "hello"') + captured = capfd.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``byte`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfdbinary): + os.system('echo "hello"') + captured = capfdbinary.readouterr() + assert captured.out == b"hello\n" + + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() diff --git a/.venv/lib/python3.11/site-packages/_pytest/compat.py b/.venv/lib/python3.11/site-packages/_pytest/compat.py new file mode 100644 index 00000000..bef8c317 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/compat.py @@ -0,0 +1,333 @@ +# mypy: allow-untyped-defs +"""Python version compatibility code.""" + +from __future__ import annotations + +from collections.abc import Callable +import enum +import functools +import inspect +from inspect import Parameter +from inspect import Signature +import os +from pathlib import Path +import sys +from typing import Any +from typing import Final +from typing import NoReturn + +import py + + +if sys.version_info >= (3, 14): + from annotationlib import Format + + +#: constant to prepare valuing pylib path replacements/lazy proxies later on +# intended for removal in pytest 8.0 or 9.0 + +# fmt: off +# intentional space to create a fake difference for the verification +LEGACY_PATH = py.path. local +# fmt: on + + +def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH: + """Internal wrapper to prepare lazy proxies for legacy_path instances""" + return LEGACY_PATH(path) + + +# fmt: off +# Singleton type for NOTSET, as described in: +# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions +class NotSetType(enum.Enum): + token = 0 +NOTSET: Final = NotSetType.token +# fmt: on + + +def iscoroutinefunction(func: object) -> bool: + """Return True if func is a coroutine function (a function defined with async + def syntax, and doesn't contain yield), or a function decorated with + @asyncio.coroutine. + + Note: copied and modified from Python 3.5's builtin coroutines.py to avoid + importing asyncio directly, which in turns also initializes the "logging" + module as a side-effect (see issue #8). + """ + return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) + + +def is_async_function(func: object) -> bool: + """Return True if the given function seems to be an async function or + an async generator.""" + return iscoroutinefunction(func) or inspect.isasyncgenfunction(func) + + +def signature(obj: Callable[..., Any]) -> Signature: + """Return signature without evaluating annotations.""" + if sys.version_info >= (3, 14): + return inspect.signature(obj, annotation_format=Format.STRING) + return inspect.signature(obj) + + +def getlocation(function, curdir: str | os.PathLike[str] | None = None) -> str: + function = get_real_func(function) + fn = Path(inspect.getfile(function)) + lineno = function.__code__.co_firstlineno + if curdir is not None: + try: + relfn = fn.relative_to(curdir) + except ValueError: + pass + else: + return f"{relfn}:{lineno + 1}" + return f"{fn}:{lineno + 1}" + + +def num_mock_patch_args(function) -> int: + """Return number of arguments used up by mock arguments (if any).""" + patchings = getattr(function, "patchings", None) + if not patchings: + return 0 + + mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) + ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) + + return len( + [ + p + for p in patchings + if not p.attribute_name + and (p.new is mock_sentinel or p.new is ut_mock_sentinel) + ] + ) + + +def getfuncargnames( + function: Callable[..., object], + *, + name: str = "", + cls: type | None = None, +) -> tuple[str, ...]: + """Return the names of a function's mandatory arguments. + + Should return the names of all function arguments that: + * Aren't bound to an instance or type as in instance or class methods. + * Don't have default values. + * Aren't bound with functools.partial. + * Aren't replaced with mocks. + + The cls arguments indicate that the function should be treated as a bound + method even though it's not unless the function is a static method. + + The name parameter should be the original name in which the function was collected. + """ + # TODO(RonnyPfannschmidt): This function should be refactored when we + # revisit fixtures. The fixture mechanism should ask the node for + # the fixture names, and not try to obtain directly from the + # function object well after collection has occurred. + + # The parameters attribute of a Signature object contains an + # ordered mapping of parameter names to Parameter instances. This + # creates a tuple of the names of the parameters that don't have + # defaults. + try: + parameters = signature(function).parameters.values() + except (ValueError, TypeError) as e: + from _pytest.outcomes import fail + + fail( + f"Could not determine arguments of {function!r}: {e}", + pytrace=False, + ) + + arg_names = tuple( + p.name + for p in parameters + if ( + p.kind is Parameter.POSITIONAL_OR_KEYWORD + or p.kind is Parameter.KEYWORD_ONLY + ) + and p.default is Parameter.empty + ) + if not name: + name = function.__name__ + + # If this function should be treated as a bound method even though + # it's passed as an unbound method or function, and its first parameter + # wasn't defined as positional only, remove the first parameter name. + if not any(p.kind is Parameter.POSITIONAL_ONLY for p in parameters) and ( + # Not using `getattr` because we don't want to resolve the staticmethod. + # Not using `cls.__dict__` because we want to check the entire MRO. + cls + and not isinstance( + inspect.getattr_static(cls, name, default=None), staticmethod + ) + ): + arg_names = arg_names[1:] + # Remove any names that will be replaced with mocks. + if hasattr(function, "__wrapped__"): + arg_names = arg_names[num_mock_patch_args(function) :] + return arg_names + + +def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]: + # Note: this code intentionally mirrors the code at the beginning of + # getfuncargnames, to get the arguments which were excluded from its result + # because they had default values. + return tuple( + p.name + for p in signature(function).parameters.values() + if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) + and p.default is not Parameter.empty + ) + + +_non_printable_ascii_translate_table = { + i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) +} +_non_printable_ascii_translate_table.update( + {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} +) + + +def ascii_escaped(val: bytes | str) -> str: + r"""If val is pure ASCII, return it as an str, otherwise, escape + bytes objects into a sequence of escaped bytes: + + b'\xc3\xb4\xc5\xd6' -> r'\xc3\xb4\xc5\xd6' + + and escapes strings into a sequence of escaped unicode ids, e.g.: + + r'4\nV\U00043efa\x0eMXWB\x1e\u3028\u15fd\xcd\U0007d944' + + Note: + The obvious "v.decode('unicode-escape')" will return + valid UTF-8 unicode if it finds them in bytes, but we + want to return escaped bytes for any byte, even if they match + a UTF-8 string. + """ + if isinstance(val, bytes): + ret = val.decode("ascii", "backslashreplace") + else: + ret = val.encode("unicode_escape").decode("ascii") + return ret.translate(_non_printable_ascii_translate_table) + + +def get_real_func(obj): + """Get the real function object of the (possibly) wrapped object by + :func:`functools.wraps`, or :func:`functools.partial`.""" + obj = inspect.unwrap(obj) + + if isinstance(obj, functools.partial): + obj = obj.func + return obj + + +def getimfunc(func): + try: + return func.__func__ + except AttributeError: + return func + + +def safe_getattr(object: Any, name: str, default: Any) -> Any: + """Like getattr but return default upon any Exception or any OutcomeException. + + Attribute access can potentially fail for 'evil' Python objects. + See issue #214. + It catches OutcomeException because of #2490 (issue #580), new outcomes + are derived from BaseException instead of Exception (for more details + check #2707). + """ + from _pytest.outcomes import TEST_OUTCOME + + try: + return getattr(object, name, default) + except TEST_OUTCOME: + return default + + +def safe_isclass(obj: object) -> bool: + """Ignore any exception via isinstance on Python 3.""" + try: + return inspect.isclass(obj) + except Exception: + return False + + +def get_user_id() -> int | None: + """Return the current process's real user id or None if it could not be + determined. + + :return: The user id or None if it could not be determined. + """ + # mypy follows the version and platform checking expectation of PEP 484: + # https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks + # Containment checks are too complex for mypy v1.5.0 and cause failure. + if sys.platform == "win32" or sys.platform == "emscripten": + # win32 does not have a getuid() function. + # Emscripten has a return 0 stub. + return None + else: + # On other platforms, a return value of -1 is assumed to indicate that + # the current process's real user id could not be determined. + ERROR = -1 + uid = os.getuid() + return uid if uid != ERROR else None + + +# Perform exhaustiveness checking. +# +# Consider this example: +# +# MyUnion = Union[int, str] +# +# def handle(x: MyUnion) -> int { +# if isinstance(x, int): +# return 1 +# elif isinstance(x, str): +# return 2 +# else: +# raise Exception('unreachable') +# +# Now suppose we add a new variant: +# +# MyUnion = Union[int, str, bytes] +# +# After doing this, we must remember ourselves to go and update the handle +# function to handle the new variant. +# +# With `assert_never` we can do better: +# +# // raise Exception('unreachable') +# return assert_never(x) +# +# Now, if we forget to handle the new variant, the type-checker will emit a +# compile-time error, instead of the runtime error we would have gotten +# previously. +# +# This also work for Enums (if you use `is` to compare) and Literals. +def assert_never(value: NoReturn) -> NoReturn: + assert False, f"Unhandled value: {value} ({type(value).__name__})" + + +class CallableBool: + """ + A bool-like object that can also be called, returning its true/false value. + + Used for backwards compatibility in cases where something was supposed to be a method + but was implemented as a simple attribute by mistake (see `TerminalReporter.isatty`). + + Do not use in new code. + """ + + def __init__(self, value: bool) -> None: + self._value = value + + def __bool__(self) -> bool: + return self._value + + def __call__(self) -> bool: + return self._value diff --git a/.venv/lib/python3.11/site-packages/_pytest/config/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/config/__init__.py new file mode 100644 index 00000000..468018fa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/config/__init__.py @@ -0,0 +1,2029 @@ +# mypy: allow-untyped-defs +"""Command line options, ini-file and conftest.py processing.""" + +from __future__ import annotations + +import argparse +import collections.abc +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence +import contextlib +import copy +import dataclasses +import enum +from functools import lru_cache +import glob +import importlib.metadata +import inspect +import os +import pathlib +import re +import shlex +import sys +from textwrap import dedent +import types +from types import FunctionType +from typing import Any +from typing import cast +from typing import Final +from typing import final +from typing import IO +from typing import TextIO +from typing import TYPE_CHECKING +import warnings + +import pluggy +from pluggy import HookimplMarker +from pluggy import HookimplOpts +from pluggy import HookspecMarker +from pluggy import HookspecOpts +from pluggy import PluginManager + +from .compat import PathAwareHookProxy +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import determine_setup +from _pytest import __version__ +import _pytest._code +from _pytest._code import ExceptionInfo +from _pytest._code import filter_traceback +from _pytest._code.code import TracebackStyle +from _pytest._io import TerminalWriter +from _pytest.config.argparsing import Argument +from _pytest.config.argparsing import Parser +import _pytest.deprecated +import _pytest.hookspec +from _pytest.outcomes import fail +from _pytest.outcomes import Skipped +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportMode +from _pytest.pathlib import resolve_package_path +from _pytest.pathlib import safe_exists +from _pytest.stash import Stash +from _pytest.warning_types import PytestConfigWarning +from _pytest.warning_types import warn_explicit_for + + +if TYPE_CHECKING: + from _pytest.assertion.rewrite import AssertionRewritingHook + from _pytest.cacheprovider import Cache + from _pytest.terminal import TerminalReporter + +_PluggyPlugin = object +"""A type to represent plugin objects. + +Plugins can be any namespace, so we can't narrow it down much, but we use an +alias to make the intent clear. + +Ideally this type would be provided by pluggy itself. +""" + + +hookimpl = HookimplMarker("pytest") +hookspec = HookspecMarker("pytest") + + +@final +class ExitCode(enum.IntEnum): + """Encodes the valid exit codes by pytest. + + Currently users and plugins may supply other exit codes as well. + + .. versionadded:: 5.0 + """ + + #: Tests passed. + OK = 0 + #: Tests failed. + TESTS_FAILED = 1 + #: pytest was interrupted. + INTERRUPTED = 2 + #: An internal error got in the way. + INTERNAL_ERROR = 3 + #: pytest was misused. + USAGE_ERROR = 4 + #: pytest couldn't find tests. + NO_TESTS_COLLECTED = 5 + + +class ConftestImportFailure(Exception): + def __init__( + self, + path: pathlib.Path, + *, + cause: Exception, + ) -> None: + self.path = path + self.cause = cause + + def __str__(self) -> str: + return f"{type(self.cause).__name__}: {self.cause} (from {self.path})" + + +def filter_traceback_for_conftest_import_failure( + entry: _pytest._code.TracebackEntry, +) -> bool: + """Filter tracebacks entries which point to pytest internals or importlib. + + Make a special case for importlib because we use it to import test modules and conftest files + in _pytest.pathlib.import_path. + """ + return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) + + +def main( + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> int | ExitCode: + """Perform an in-process test run. + + :param args: + List of command line arguments. If `None` or not given, defaults to reading + arguments directly from the process command line (:data:`sys.argv`). + :param plugins: List of plugin objects to be auto-registered during initialization. + + :returns: An exit code. + """ + old_pytest_version = os.environ.get("PYTEST_VERSION") + try: + os.environ["PYTEST_VERSION"] = __version__ + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure as e: + exc_info = ExceptionInfo.from_exception(e.cause) + tw = TerminalWriter(sys.stderr) + tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) + exc_info.traceback = exc_info.traceback.filter( + filter_traceback_for_conftest_import_failure + ) + exc_repr = ( + exc_info.getrepr(style="short", chain=False) + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + for line in formatted_tb.splitlines(): + tw.line(line.rstrip(), red=True) + return ExitCode.USAGE_ERROR + else: + try: + ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) + try: + return ExitCode(ret) + except ValueError: + return ret + finally: + config._ensure_unconfigure() + except UsageError as e: + tw = TerminalWriter(sys.stderr) + for msg in e.args: + tw.line(f"ERROR: {msg}\n", red=True) + return ExitCode.USAGE_ERROR + finally: + if old_pytest_version is None: + os.environ.pop("PYTEST_VERSION", None) + else: + os.environ["PYTEST_VERSION"] = old_pytest_version + + +def console_main() -> int: + """The CLI entry point of pytest. + + This function is not meant for programmable use; use `main()` instead. + """ + # https://docs.python.org/3/library/signal.html#note-on-sigpipe + try: + code = main() + sys.stdout.flush() + return code + except BrokenPipeError: + # Python flushes standard streams on exit; redirect remaining output + # to devnull to avoid another BrokenPipeError at shutdown + devnull = os.open(os.devnull, os.O_WRONLY) + os.dup2(devnull, sys.stdout.fileno()) + return 1 # Python exits with error code 1 on EPIPE + + +class cmdline: # compatibility namespace + main = staticmethod(main) + + +def filename_arg(path: str, optname: str) -> str: + """Argparse type validator for filename arguments. + + :path: Path of filename. + :optname: Name of the option. + """ + if os.path.isdir(path): + raise UsageError(f"{optname} must be a filename, given: {path}") + return path + + +def directory_arg(path: str, optname: str) -> str: + """Argparse type validator for directory arguments. + + :path: Path of directory. + :optname: Name of the option. + """ + if not os.path.isdir(path): + raise UsageError(f"{optname} must be a directory, given: {path}") + return path + + +# Plugins that cannot be disabled via "-p no:X" currently. +essential_plugins = ( + "mark", + "main", + "runner", + "fixtures", + "helpconfig", # Provides -p. +) + +default_plugins = ( + *essential_plugins, + "python", + "terminal", + "debugging", + "unittest", + "capture", + "skipping", + "legacypath", + "tmpdir", + "monkeypatch", + "recwarn", + "pastebin", + "assertion", + "junitxml", + "doctest", + "cacheprovider", + "freeze_support", + "setuponly", + "setupplan", + "stepwise", + "unraisableexception", + "threadexception", + "warnings", + "logging", + "reports", + "faulthandler", +) + +builtin_plugins = { + *default_plugins, + "pytester", + "pytester_assertions", +} + + +def get_config( + args: list[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + # subsequent calls to main will create a fresh instance + pluginmanager = PytestPluginManager() + config = Config( + pluginmanager, + invocation_params=Config.InvocationParams( + args=args or (), + plugins=plugins, + dir=pathlib.Path.cwd(), + ), + ) + + if args is not None: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(args, exclude_only=True) + + for spec in default_plugins: + pluginmanager.import_plugin(spec) + + return config + + +def get_plugin_manager() -> PytestPluginManager: + """Obtain a new instance of the + :py:class:`pytest.PytestPluginManager`, with default plugins + already loaded. + + This function can be used by integration with other tools, like hooking + into pytest to run tests into an IDE. + """ + return get_config().pluginmanager + + +def _prepareconfig( + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + if args is None: + args = sys.argv[1:] + elif isinstance(args, os.PathLike): + args = [os.fspath(args)] + elif not isinstance(args, list): + msg = ( # type:ignore[unreachable] + "`args` parameter expected to be a list of strings, got: {!r} (type: {})" + ) + raise TypeError(msg.format(args, type(args))) + + config = get_config(args, plugins) + pluginmanager = config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, str): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + config = pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args + ) + return config + except BaseException: + config._ensure_unconfigure() + raise + + +def _get_directory(path: pathlib.Path) -> pathlib.Path: + """Get the directory of a path - itself if already a directory.""" + if path.is_file(): + return path.parent + else: + return path + + +def _get_legacy_hook_marks( + method: Any, + hook_type: str, + opt_names: tuple[str, ...], +) -> dict[str, bool]: + if TYPE_CHECKING: + # abuse typeguard from importlib to avoid massive method type union that's lacking an alias + assert inspect.isroutine(method) + known_marks: set[str] = {m.name for m in getattr(method, "pytestmark", [])} + must_warn: list[str] = [] + opts: dict[str, bool] = {} + for opt_name in opt_names: + opt_attr = getattr(method, opt_name, AttributeError) + if opt_attr is not AttributeError: + must_warn.append(f"{opt_name}={opt_attr}") + opts[opt_name] = True + elif opt_name in known_marks: + must_warn.append(f"{opt_name}=True") + opts[opt_name] = True + else: + opts[opt_name] = False + if must_warn: + hook_opts = ", ".join(must_warn) + message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( + type=hook_type, + fullname=method.__qualname__, + hook_opts=hook_opts, + ) + warn_explicit_for(cast(FunctionType, method), message) + return opts + + +@final +class PytestPluginManager(PluginManager): + """A :py:class:`pluggy.PluginManager ` with + additional pytest-specific functionality: + + * Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and + ``pytest_plugins`` global variables found in plugins being loaded. + * ``conftest.py`` loading during start-up. + """ + + def __init__(self) -> None: + from _pytest.assertion import DummyRewriteHook + from _pytest.assertion import RewriteHook + + super().__init__("pytest") + + # -- State related to local conftest plugins. + # All loaded conftest modules. + self._conftest_plugins: set[types.ModuleType] = set() + # All conftest modules applicable for a directory. + # This includes the directory's own conftest modules as well + # as those of its parent directories. + self._dirpath2confmods: dict[pathlib.Path, list[types.ModuleType]] = {} + # Cutoff directory above which conftests are no longer discovered. + self._confcutdir: pathlib.Path | None = None + # If set, conftest loading is skipped. + self._noconftest = False + + # _getconftestmodules()'s call to _get_directory() causes a stat + # storm when it's called potentially thousands of times in a test + # session (#9478), often with the same path, so cache it. + self._get_directory = lru_cache(256)(_get_directory) + + # plugins that were explicitly skipped with pytest.skip + # list of (module name, skip reason) + # previously we would issue a warning when a plugin was skipped, but + # since we refactored warnings as first citizens of Config, they are + # just stored here to be used later. + self.skipped_plugins: list[tuple[str, str]] = [] + + self.add_hookspecs(_pytest.hookspec) + self.register(self) + if os.environ.get("PYTEST_DEBUG"): + err: IO[str] = sys.stderr + encoding: str = getattr(err, "encoding", "utf8") + try: + err = open( + os.dup(err.fileno()), + mode=err.mode, + buffering=1, + encoding=encoding, + ) + except Exception: + pass + self.trace.root.setwriter(err.write) + self.enable_tracing() + + # Config._consider_importhook will set a real object if required. + self.rewrite_hook: RewriteHook = DummyRewriteHook() + # Used to know when we are importing conftests after the pytest_configure stage. + self._configured = False + + def parse_hookimpl_opts( + self, plugin: _PluggyPlugin, name: str + ) -> HookimplOpts | None: + """:meta private:""" + # pytest hooks are always prefixed with "pytest_", + # so we avoid accessing possibly non-readable attributes + # (see issue #1073). + if not name.startswith("pytest_"): + return None + # Ignore names which cannot be hooks. + if name == "pytest_plugins": + return None + + opts = super().parse_hookimpl_opts(plugin, name) + if opts is not None: + return opts + + method = getattr(plugin, name) + # Consider only actual functions for hooks (#3775). + if not inspect.isroutine(method): + return None + # Collect unmarked hooks as long as they have the `pytest_' prefix. + legacy = _get_legacy_hook_marks( + method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") + ) + return cast(HookimplOpts, legacy) + + def parse_hookspec_opts(self, module_or_class, name: str) -> HookspecOpts | None: + """:meta private:""" + opts = super().parse_hookspec_opts(module_or_class, name) + if opts is None: + method = getattr(module_or_class, name) + if name.startswith("pytest_"): + legacy = _get_legacy_hook_marks( + method, "spec", ("firstresult", "historic") + ) + opts = cast(HookspecOpts, legacy) + return opts + + def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None: + if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: + warnings.warn( + PytestConfigWarning( + "{} plugin has been merged into the core, " + "please remove it from your requirements.".format( + name.replace("_", "-") + ) + ) + ) + return None + plugin_name = super().register(plugin, name) + if plugin_name is not None: + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict( + plugin=plugin, + plugin_name=plugin_name, + manager=self, + ) + ) + + if isinstance(plugin, types.ModuleType): + self.consider_module(plugin) + return plugin_name + + def getplugin(self, name: str): + # Support deprecated naming because plugins (xdist e.g.) use it. + plugin: _PluggyPlugin | None = self.get_plugin(name) + return plugin + + def hasplugin(self, name: str) -> bool: + """Return whether a plugin with the given name is registered.""" + return bool(self.get_plugin(name)) + + def pytest_configure(self, config: Config) -> None: + """:meta private:""" + # XXX now that the pluginmanager exposes hookimpl(tryfirst...) + # we should remove tryfirst/trylast as markers. + config.addinivalue_line( + "markers", + "tryfirst: mark a hook implementation function such that the " + "plugin machinery will try to call it first/as early as possible. " + "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", + ) + config.addinivalue_line( + "markers", + "trylast: mark a hook implementation function such that the " + "plugin machinery will try to call it last/as late as possible. " + "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", + ) + self._configured = True + + # + # Internal API for local conftest plugin handling. + # + def _set_initial_conftests( + self, + args: Sequence[str | pathlib.Path], + pyargs: bool, + noconftest: bool, + rootpath: pathlib.Path, + confcutdir: pathlib.Path | None, + invocation_dir: pathlib.Path, + importmode: ImportMode | str, + *, + consider_namespace_packages: bool, + ) -> None: + """Load initial conftest files given a preparsed "namespace". + + As conftest files may add their own command line options which have + arguments ('--my-opt somepath') we might get some false positives. + All builtin and 3rd party plugins will have been loaded, however, so + common options will not confuse our logic here. + """ + self._confcutdir = ( + absolutepath(invocation_dir / confcutdir) if confcutdir else None + ) + self._noconftest = noconftest + self._using_pyargs = pyargs + foundanchor = False + for initial_path in args: + path = str(initial_path) + # remove node-id syntax + i = path.find("::") + if i != -1: + path = path[:i] + anchor = absolutepath(invocation_dir / path) + + # Ensure we do not break if what appears to be an anchor + # is in fact a very long option (#10169, #11394). + if safe_exists(anchor): + self._try_load_conftest( + anchor, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + foundanchor = True + if not foundanchor: + self._try_load_conftest( + invocation_dir, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + + def _is_in_confcutdir(self, path: pathlib.Path) -> bool: + """Whether to consider the given path to load conftests from.""" + if self._confcutdir is None: + return True + # The semantics here are literally: + # Do not load a conftest if it is found upwards from confcut dir. + # But this is *not* the same as: + # Load only conftests from confcutdir or below. + # At first glance they might seem the same thing, however we do support use cases where + # we want to load conftests that are not found in confcutdir or below, but are found + # in completely different directory hierarchies like packages installed + # in out-of-source trees. + # (see #9767 for a regression where the logic was inverted). + return path not in self._confcutdir.parents + + def _try_load_conftest( + self, + anchor: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> None: + self._loadconftestmodules( + anchor, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + # let's also consider test* subdirs + if anchor.is_dir(): + for x in anchor.glob("test*"): + if x.is_dir(): + self._loadconftestmodules( + x, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + + def _loadconftestmodules( + self, + path: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> None: + if self._noconftest: + return + + directory = self._get_directory(path) + + # Optimization: avoid repeated searches in the same directory. + # Assumes always called with same importmode and rootpath. + if directory in self._dirpath2confmods: + return + + clist = [] + for parent in reversed((directory, *directory.parents)): + if self._is_in_confcutdir(parent): + conftestpath = parent / "conftest.py" + if conftestpath.is_file(): + mod = self._importconftest( + conftestpath, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + clist.append(mod) + self._dirpath2confmods[directory] = clist + + def _getconftestmodules(self, path: pathlib.Path) -> Sequence[types.ModuleType]: + directory = self._get_directory(path) + return self._dirpath2confmods.get(directory, ()) + + def _rget_with_confmod( + self, + name: str, + path: pathlib.Path, + ) -> tuple[types.ModuleType, Any]: + modules = self._getconftestmodules(path) + for mod in reversed(modules): + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def _importconftest( + self, + conftestpath: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> types.ModuleType: + conftestpath_plugin_name = str(conftestpath) + existing = self.get_plugin(conftestpath_plugin_name) + if existing is not None: + return cast(types.ModuleType, existing) + + # conftest.py files there are not in a Python package all have module + # name "conftest", and thus conflict with each other. Clear the existing + # before loading the new one, otherwise the existing one will be + # returned from the module cache. + pkgpath = resolve_package_path(conftestpath) + if pkgpath is None: + try: + del sys.modules[conftestpath.stem] + except KeyError: + pass + + try: + mod = import_path( + conftestpath, + mode=importmode, + root=rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + except Exception as e: + assert e.__traceback__ is not None + raise ConftestImportFailure(conftestpath, cause=e) from e + + self._check_non_top_pytest_plugins(mod, conftestpath) + + self._conftest_plugins.add(mod) + dirpath = conftestpath.parent + if dirpath in self._dirpath2confmods: + for path, mods in self._dirpath2confmods.items(): + if dirpath in path.parents or path == dirpath: + if mod in mods: + raise AssertionError( + f"While trying to load conftest path {conftestpath!s}, " + f"found that the module {mod} is already loaded with path {mod.__file__}. " + "This is not supposed to happen. Please report this issue to pytest." + ) + mods.append(mod) + self.trace(f"loading conftestmodule {mod!r}") + self.consider_conftest(mod, registration_name=conftestpath_plugin_name) + return mod + + def _check_non_top_pytest_plugins( + self, + mod: types.ModuleType, + conftestpath: pathlib.Path, + ) -> None: + if ( + hasattr(mod, "pytest_plugins") + and self._configured + and not self._using_pyargs + ): + msg = ( + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" + "It affects the entire test suite instead of just below the conftest as expected.\n" + " {}\n" + "Please move it to a top level conftest file at the rootdir:\n" + " {}\n" + "For more information, visit:\n" + " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" + ) + fail(msg.format(conftestpath, self._confcutdir), pytrace=False) + + # + # API for bootstrapping plugin loading + # + # + + def consider_preparse( + self, args: Sequence[str], *, exclude_only: bool = False + ) -> None: + """:meta private:""" + i = 0 + n = len(args) + while i < n: + opt = args[i] + i += 1 + if isinstance(opt, str): + if opt == "-p": + try: + parg = args[i] + except IndexError: + return + i += 1 + elif opt.startswith("-p"): + parg = opt[2:] + else: + continue + parg = parg.strip() + if exclude_only and not parg.startswith("no:"): + continue + self.consider_pluginarg(parg) + + def consider_pluginarg(self, arg: str) -> None: + """:meta private:""" + if arg.startswith("no:"): + name = arg[3:] + if name in essential_plugins: + raise UsageError(f"plugin {name} cannot be disabled") + + # PR #4304: remove stepwise if cacheprovider is blocked. + if name == "cacheprovider": + self.set_blocked("stepwise") + self.set_blocked("pytest_stepwise") + + self.set_blocked(name) + if not name.startswith("pytest_"): + self.set_blocked("pytest_" + name) + else: + name = arg + # Unblock the plugin. + self.unblock(name) + if not name.startswith("pytest_"): + self.unblock("pytest_" + name) + self.import_plugin(arg, consider_entry_points=True) + + def consider_conftest( + self, conftestmodule: types.ModuleType, registration_name: str + ) -> None: + """:meta private:""" + self.register(conftestmodule, name=registration_name) + + def consider_env(self) -> None: + """:meta private:""" + self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + + def consider_module(self, mod: types.ModuleType) -> None: + """:meta private:""" + self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) + + def _import_plugin_specs( + self, spec: None | types.ModuleType | str | Sequence[str] + ) -> None: + plugins = _get_plugin_specs_as_list(spec) + for import_spec in plugins: + self.import_plugin(import_spec) + + def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: + """Import a plugin with ``modname``. + + If ``consider_entry_points`` is True, entry point names are also + considered to find a plugin. + """ + # Most often modname refers to builtin modules, e.g. "pytester", + # "terminal" or "capture". Those plugins are registered under their + # basename for historic purposes but must be imported with the + # _pytest prefix. + assert isinstance(modname, str), ( + f"module name as text required, got {modname!r}" + ) + if self.is_blocked(modname) or self.get_plugin(modname) is not None: + return + + importspec = "_pytest." + modname if modname in builtin_plugins else modname + self.rewrite_hook.mark_rewrite(importspec) + + if consider_entry_points: + loaded = self.load_setuptools_entrypoints("pytest11", name=modname) + if loaded: + return + + try: + __import__(importspec) + except ImportError as e: + raise ImportError( + f'Error importing plugin "{modname}": {e.args[0]}' + ).with_traceback(e.__traceback__) from e + + except Skipped as e: + self.skipped_plugins.append((modname, e.msg or "")) + else: + mod = sys.modules[importspec] + self.register(mod, modname) + + +def _get_plugin_specs_as_list( + specs: None | types.ModuleType | str | Sequence[str], +) -> list[str]: + """Parse a plugins specification into a list of plugin names.""" + # None means empty. + if specs is None: + return [] + # Workaround for #3899 - a submodule which happens to be called "pytest_plugins". + if isinstance(specs, types.ModuleType): + return [] + # Comma-separated list. + if isinstance(specs, str): + return specs.split(",") if specs else [] + # Direct specification. + if isinstance(specs, collections.abc.Sequence): + return list(specs) + raise UsageError( + f"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: {specs!r}" + ) + + +class Notset: + def __repr__(self): + return "" + + +notset = Notset() + + +def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: + """Given an iterable of file names in a source distribution, return the "names" that should + be marked for assertion rewrite. + + For example the package "pytest_mock/__init__.py" should be added as "pytest_mock" in + the assertion rewrite mechanism. + + This function has to deal with dist-info based distributions and egg based distributions + (which are still very much in use for "editable" installs). + + Here are the file names as seen in a dist-info based distribution: + + pytest_mock/__init__.py + pytest_mock/_version.py + pytest_mock/plugin.py + pytest_mock.egg-info/PKG-INFO + + Here are the file names as seen in an egg based distribution: + + src/pytest_mock/__init__.py + src/pytest_mock/_version.py + src/pytest_mock/plugin.py + src/pytest_mock.egg-info/PKG-INFO + LICENSE + setup.py + + We have to take in account those two distribution flavors in order to determine which + names should be considered for assertion rewriting. + + More information: + https://github.com/pytest-dev/pytest-mock/issues/167 + """ + package_files = list(package_files) + seen_some = False + for fn in package_files: + is_simple_module = "/" not in fn and fn.endswith(".py") + is_package = fn.count("/") == 1 and fn.endswith("__init__.py") + if is_simple_module: + module_name, _ = os.path.splitext(fn) + # we ignore "setup.py" at the root of the distribution + # as well as editable installation finder modules made by setuptools + if module_name != "setup" and not module_name.startswith("__editable__"): + seen_some = True + yield module_name + elif is_package: + package_name = os.path.dirname(fn) + seen_some = True + yield package_name + + if not seen_some: + # At this point we did not find any packages or modules suitable for assertion + # rewriting, so we try again by stripping the first path component (to account for + # "src" based source trees for example). + # This approach lets us have the common case continue to be fast, as egg-distributions + # are rarer. + new_package_files = [] + for fn in package_files: + parts = fn.split("/") + new_fn = "/".join(parts[1:]) + if new_fn: + new_package_files.append(new_fn) + if new_package_files: + yield from _iter_rewritable_modules(new_package_files) + + +@final +class Config: + """Access to configuration values, pluginmanager and plugin hooks. + + :param PytestPluginManager pluginmanager: + A pytest PluginManager. + + :param InvocationParams invocation_params: + Object containing parameters regarding the :func:`pytest.main` + invocation. + """ + + @final + @dataclasses.dataclass(frozen=True) + class InvocationParams: + """Holds parameters passed during :func:`pytest.main`. + + The object attributes are read-only. + + .. versionadded:: 5.1 + + .. note:: + + Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` + ini option are handled by pytest, not being included in the ``args`` attribute. + + Plugins accessing ``InvocationParams`` must be aware of that. + """ + + args: tuple[str, ...] + """The command-line arguments as passed to :func:`pytest.main`.""" + plugins: Sequence[str | _PluggyPlugin] | None + """Extra plugins, might be `None`.""" + dir: pathlib.Path + """The directory from which :func:`pytest.main` was invoked. :type: pathlib.Path""" + + def __init__( + self, + *, + args: Iterable[str], + plugins: Sequence[str | _PluggyPlugin] | None, + dir: pathlib.Path, + ) -> None: + object.__setattr__(self, "args", tuple(args)) + object.__setattr__(self, "plugins", plugins) + object.__setattr__(self, "dir", dir) + + class ArgsSource(enum.Enum): + """Indicates the source of the test arguments. + + .. versionadded:: 7.2 + """ + + #: Command line arguments. + ARGS = enum.auto() + #: Invocation directory. + INVOCATION_DIR = enum.auto() + INCOVATION_DIR = INVOCATION_DIR # backwards compatibility alias + #: 'testpaths' configuration value. + TESTPATHS = enum.auto() + + # Set by cacheprovider plugin. + cache: Cache + + def __init__( + self, + pluginmanager: PytestPluginManager, + *, + invocation_params: InvocationParams | None = None, + ) -> None: + from .argparsing import FILE_OR_DIR + from .argparsing import Parser + + if invocation_params is None: + invocation_params = self.InvocationParams( + args=(), plugins=None, dir=pathlib.Path.cwd() + ) + + self.option = argparse.Namespace() + """Access to command line option as attributes. + + :type: argparse.Namespace + """ + + self.invocation_params = invocation_params + """The parameters with which pytest was invoked. + + :type: InvocationParams + """ + + _a = FILE_OR_DIR + self._parser = Parser( + usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", + processopt=self._processopt, + _ispytest=True, + ) + self.pluginmanager = pluginmanager + """The plugin manager handles plugin registration and hook invocation. + + :type: PytestPluginManager + """ + + self.stash = Stash() + """A place where plugins can store information on the config for their + own use. + + :type: Stash + """ + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + + self.trace = self.pluginmanager.trace.root.get("config") + self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] + self._inicache: dict[str, Any] = {} + self._override_ini: Sequence[str] = () + self._opt2dest: dict[str, str] = {} + self._cleanup_stack = contextlib.ExitStack() + self.pluginmanager.register(self, "pytestconfig") + self._configured = False + self.hook.pytest_addoption.call_historic( + kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) + ) + self.args_source = Config.ArgsSource.ARGS + self.args: list[str] = [] + + @property + def rootpath(self) -> pathlib.Path: + """The path to the :ref:`rootdir `. + + :type: pathlib.Path + + .. versionadded:: 6.1 + """ + return self._rootpath + + @property + def inipath(self) -> pathlib.Path | None: + """The path to the :ref:`configfile `. + + .. versionadded:: 6.1 + """ + return self._inipath + + def add_cleanup(self, func: Callable[[], None]) -> None: + """Add a function to be called when the config object gets out of + use (usually coinciding with pytest_unconfigure). + """ + self._cleanup_stack.callback(func) + + def _do_configure(self) -> None: + assert not self._configured + self._configured = True + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) + + def _ensure_unconfigure(self) -> None: + try: + if self._configured: + self._configured = False + try: + self.hook.pytest_unconfigure(config=self) + finally: + self.hook.pytest_configure._call_history = [] + finally: + try: + self._cleanup_stack.close() + finally: + self._cleanup_stack = contextlib.ExitStack() + + def get_terminal_writer(self) -> TerminalWriter: + terminalreporter: TerminalReporter | None = self.pluginmanager.get_plugin( + "terminalreporter" + ) + assert terminalreporter is not None + return terminalreporter._tw + + def pytest_cmdline_parse( + self, pluginmanager: PytestPluginManager, args: list[str] + ) -> Config: + try: + self.parse(args) + except UsageError: + # Handle --version and --help here in a minimal fashion. + # This gets done via helpconfig normally, but its + # pytest_cmdline_main is not called in case of errors. + if getattr(self.option, "version", False) or "--version" in args: + from _pytest.helpconfig import showversion + + showversion(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser._getparser().print_help() + sys.stdout.write( + "\nNOTE: displaying only minimal help due to UsageError.\n\n" + ) + + raise + + return self + + def notify_exception( + self, + excinfo: ExceptionInfo[BaseException], + option: argparse.Namespace | None = None, + ) -> None: + if option and getattr(option, "fulltrace", False): + style: TracebackStyle = "long" + else: + style = "native" + excrepr = excinfo.getrepr( + funcargs=True, showlocals=getattr(option, "showlocals", False), style=style + ) + res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) + if not any(res): + for line in str(excrepr).split("\n"): + sys.stderr.write(f"INTERNALERROR> {line}\n") + sys.stderr.flush() + + def cwd_relative_nodeid(self, nodeid: str) -> str: + # nodeid's are relative to the rootpath, compute relative to cwd. + if self.invocation_params.dir != self.rootpath: + base_path_part, *nodeid_part = nodeid.split("::") + # Only process path part + fullpath = self.rootpath / base_path_part + relative_path = bestrelpath(self.invocation_params.dir, fullpath) + + nodeid = "::".join([relative_path, *nodeid_part]) + return nodeid + + @classmethod + def fromdictargs(cls, option_dict, args) -> Config: + """Constructor usable for subprocesses.""" + config = get_config(args) + config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) + for x in config.option.plugins: + config.pluginmanager.consider_pluginarg(x) + return config + + def _processopt(self, opt: Argument) -> None: + for name in opt._short_opts + opt._long_opts: + self._opt2dest[name] = opt.dest + + if hasattr(opt, "default"): + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + + @hookimpl(trylast=True) + def pytest_load_initial_conftests(self, early_config: Config) -> None: + # We haven't fully parsed the command line arguments yet, so + # early_config.args it not set yet. But we need it for + # discovering the initial conftests. So "pre-run" the logic here. + # It will be done for real in `parse()`. + args, args_source = early_config._decide_args( + args=early_config.known_args_namespace.file_or_dir, + pyargs=early_config.known_args_namespace.pyargs, + testpaths=early_config.getini("testpaths"), + invocation_dir=early_config.invocation_params.dir, + rootpath=early_config.rootpath, + warn=False, + ) + self.pluginmanager._set_initial_conftests( + args=args, + pyargs=early_config.known_args_namespace.pyargs, + noconftest=early_config.known_args_namespace.noconftest, + rootpath=early_config.rootpath, + confcutdir=early_config.known_args_namespace.confcutdir, + invocation_dir=early_config.invocation_params.dir, + importmode=early_config.known_args_namespace.importmode, + consider_namespace_packages=early_config.getini( + "consider_namespace_packages" + ), + ) + + def _initini(self, args: Sequence[str]) -> None: + ns, unknown_args = self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + rootpath, inipath, inicfg = determine_setup( + inifile=ns.inifilename, + args=ns.file_or_dir + unknown_args, + rootdir_cmd_arg=ns.rootdir or None, + invocation_dir=self.invocation_params.dir, + ) + self._rootpath = rootpath + self._inipath = inipath + self.inicfg = inicfg + self._parser.extra_info["rootdir"] = str(self.rootpath) + self._parser.extra_info["inifile"] = str(self.inipath) + self._parser.addini("addopts", "Extra command line options", "args") + self._parser.addini("minversion", "Minimally required pytest version") + self._parser.addini( + "pythonpath", type="paths", help="Add paths to sys.path", default=[] + ) + self._parser.addini( + "required_plugins", + "Plugins that must be present for pytest to run", + type="args", + default=[], + ) + self._override_ini = ns.override_ini or () + + def _consider_importhook(self, args: Sequence[str]) -> None: + """Install the PEP 302 import hook if using assertion rewriting. + + Needs to parse the --assert= option from the commandline + and find all the installed plugins to mark them for rewriting + by the importhook. + """ + ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + mode = getattr(ns, "assertmode", "plain") + + disable_autoload = getattr(ns, "disable_plugin_autoload", False) or bool( + os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD") + ) + if mode == "rewrite": + import _pytest.assertion + + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = "plain" + else: + self._mark_plugins_for_rewrite(hook, disable_autoload) + self._warn_about_missing_assertion(mode) + + def _mark_plugins_for_rewrite( + self, hook: AssertionRewritingHook, disable_autoload: bool + ) -> None: + """Given an importhook, mark for rewrite any top-level + modules or packages in the distribution package for + all pytest plugins.""" + self.pluginmanager.rewrite_hook = hook + + if disable_autoload: + # We don't autoload from distribution package entry points, + # no need to continue. + return + + package_files = ( + str(file) + for dist in importlib.metadata.distributions() + if any(ep.group == "pytest11" for ep in dist.entry_points) + for file in dist.files or [] + ) + + for name in _iter_rewritable_modules(package_files): + hook.mark_rewrite(name) + + def _configure_python_path(self) -> None: + # `pythonpath = a b` will set `sys.path` to `[a, b, x, y, z, ...]` + for path in reversed(self.getini("pythonpath")): + sys.path.insert(0, str(path)) + self.add_cleanup(self._unconfigure_python_path) + + def _unconfigure_python_path(self) -> None: + for path in self.getini("pythonpath"): + path_str = str(path) + if path_str in sys.path: + sys.path.remove(path_str) + + def _validate_args(self, args: list[str], via: str) -> list[str]: + """Validate known args.""" + self._parser._config_source_hint = via # type: ignore + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + del self._parser._config_source_hint # type: ignore + + return args + + def _decide_args( + self, + *, + args: list[str], + pyargs: bool, + testpaths: list[str], + invocation_dir: pathlib.Path, + rootpath: pathlib.Path, + warn: bool, + ) -> tuple[list[str], ArgsSource]: + """Decide the args (initial paths/nodeids) to use given the relevant inputs. + + :param warn: Whether can issue warnings. + + :returns: The args and the args source. Guaranteed to be non-empty. + """ + if args: + source = Config.ArgsSource.ARGS + result = args + else: + if invocation_dir == rootpath: + source = Config.ArgsSource.TESTPATHS + if pyargs: + result = testpaths + else: + result = [] + for path in testpaths: + result.extend(sorted(glob.iglob(path, recursive=True))) + if testpaths and not result: + if warn: + warning_text = ( + "No files were found in testpaths; " + "consider removing or adjusting your testpaths configuration. " + "Searching recursively from the current directory instead." + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), stacklevel=3 + ) + else: + result = [] + if not result: + source = Config.ArgsSource.INVOCATION_DIR + result = [str(invocation_dir)] + return result, source + + def _preparse(self, args: list[str], addopts: bool = True) -> None: + if addopts: + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = ( + self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + + args + ) + self._initini(args) + if addopts: + args[:] = ( + self._validate_args(self.getini("addopts"), "via addopts config") + args + ) + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.option) + ) + self._checkversion() + self._consider_importhook(args) + self._configure_python_path() + self.pluginmanager.consider_preparse(args, exclude_only=False) + if ( + not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD") + and not self.known_args_namespace.disable_plugin_autoload + ): + # Autoloading from distribution package entry point has + # not been disabled. + self.pluginmanager.load_setuptools_entrypoints("pytest11") + # Otherwise only plugins explicitly specified in PYTEST_PLUGINS + # are going to be loaded. + self.pluginmanager.consider_env() + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.known_args_namespace) + ) + + self._validate_plugins() + self._warn_about_skipped_plugins() + + if self.known_args_namespace.confcutdir is None: + if self.inipath is not None: + confcutdir = str(self.inipath.parent) + else: + confcutdir = str(self.rootpath) + self.known_args_namespace.confcutdir = confcutdir + try: + self.hook.pytest_load_initial_conftests( + early_config=self, args=args, parser=self._parser + ) + except ConftestImportFailure as e: + if self.known_args_namespace.help or self.known_args_namespace.version: + # we don't want to prevent --help/--version to work + # so just let it pass and print a warning at the end + self.issue_config_time_warning( + PytestConfigWarning(f"could not load initial conftests: {e.path}"), + stacklevel=2, + ) + else: + raise + + @hookimpl(wrapper=True) + def pytest_collection(self) -> Generator[None, object, object]: + # Validate invalid ini keys after collection is done so we take in account + # options added by late-loading conftest files. + try: + return (yield) + finally: + self._validate_config_options() + + def _checkversion(self) -> None: + import pytest + + minver = self.inicfg.get("minversion", None) + if minver: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if not isinstance(minver, str): + raise pytest.UsageError( + f"{self.inipath}: 'minversion' must be a single value" + ) + + if Version(minver) > Version(pytest.__version__): + raise pytest.UsageError( + f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" + ) + + def _validate_config_options(self) -> None: + for key in sorted(self._get_unknown_ini_keys()): + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + + def _validate_plugins(self) -> None: + required_plugins = sorted(self.getini("required_plugins")) + if not required_plugins: + return + + # Imported lazily to improve start-up time. + from packaging.requirements import InvalidRequirement + from packaging.requirements import Requirement + from packaging.version import Version + + plugin_info = self.pluginmanager.list_plugin_distinfo() + plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} + + missing_plugins = [] + for required_plugin in required_plugins: + try: + req = Requirement(required_plugin) + except InvalidRequirement: + missing_plugins.append(required_plugin) + continue + + if req.name not in plugin_dist_info: + missing_plugins.append(required_plugin) + elif not req.specifier.contains( + Version(plugin_dist_info[req.name]), prereleases=True + ): + missing_plugins.append(required_plugin) + + if missing_plugins: + raise UsageError( + "Missing required plugins: {}".format(", ".join(missing_plugins)), + ) + + def _warn_or_fail_if_strict(self, message: str) -> None: + if self.known_args_namespace.strict_config: + raise UsageError(message) + + self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) + + def _get_unknown_ini_keys(self) -> list[str]: + parser_inicfg = self._parser._inidict + return [name for name in self.inicfg if name not in parser_inicfg] + + def parse(self, args: list[str], addopts: bool = True) -> None: + # Parse given cmdline arguments into this config object. + assert self.args == [], ( + "can only parse cmdline args at most once per Config object" + ) + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager) + ) + self._preparse(args, addopts=addopts) + self._parser.after_preparse = True # type: ignore + try: + args = self._parser.parse_setoption( + args, self.option, namespace=self.option + ) + self.args, self.args_source = self._decide_args( + args=args, + pyargs=self.known_args_namespace.pyargs, + testpaths=self.getini("testpaths"), + invocation_dir=self.invocation_params.dir, + rootpath=self.rootpath, + warn=True, + ) + except PrintHelp: + pass + + def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: + """Issue and handle a warning during the "configure" stage. + + During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` + function because it is not possible to have hook wrappers around ``pytest_configure``. + + This function is mainly intended for plugins that need to issue warnings during + ``pytest_configure`` (or similar stages). + + :param warning: The warning instance. + :param stacklevel: stacklevel forwarded to warnings.warn. + """ + if self.pluginmanager.is_blocked("warnings"): + return + + cmdline_filters = self.known_args_namespace.pythonwarnings or [] + config_filters = self.getini("filterwarnings") + + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always", type(warning)) + apply_warning_filters(config_filters, cmdline_filters) + warnings.warn(warning, stacklevel=stacklevel) + + if records: + frame = sys._getframe(stacklevel - 1) + location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + self.hook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + nodeid="", + location=location, + ) + ) + + def addinivalue_line(self, name: str, line: str) -> None: + """Add a line to an ini-file option. The option must have been + declared but might not yet be set in which case the line becomes + the first line in its value.""" + x = self.getini(name) + assert isinstance(x, list) + x.append(line) # modifies the cached list inline + + def getini(self, name: str) -> Any: + """Return configuration value from an :ref:`ini file `. + + If a configuration value is not defined in an + :ref:`ini file `, then the ``default`` value provided while + registering the configuration through + :func:`parser.addini ` will be returned. + Please note that you can even provide ``None`` as a valid + default value. + + If ``default`` is not provided while registering using + :func:`parser.addini `, then a default value + based on the ``type`` parameter passed to + :func:`parser.addini ` will be returned. + The default values based on ``type`` are: + ``paths``, ``pathlist``, ``args`` and ``linelist`` : empty list ``[]`` + ``bool`` : ``False`` + ``string`` : empty string ``""`` + ``int`` : ``0`` + ``float`` : ``0.0`` + + If neither the ``default`` nor the ``type`` parameter is passed + while registering the configuration through + :func:`parser.addini `, then the configuration + is treated as a string and a default empty string '' is returned. + + If the specified name hasn't been registered through a prior + :func:`parser.addini ` call (usually from a + plugin), a ValueError is raised. + """ + try: + return self._inicache[name] + except KeyError: + self._inicache[name] = val = self._getini(name) + return val + + # Meant for easy monkeypatching by legacypath plugin. + # Can be inlined back (with no cover removed) once legacypath is gone. + def _getini_unknown_type(self, name: str, type: str, value: object): + msg = ( + f"Option {name} has unknown configuration type {type} with value {value!r}" + ) + raise ValueError(msg) # pragma: no cover + + def _getini(self, name: str): + try: + description, type, default = self._parser._inidict[name] + except KeyError as e: + raise ValueError(f"unknown configuration value: {name!r}") from e + override_value = self._get_override_ini_value(name) + if override_value is None: + try: + value = self.inicfg[name] + except KeyError: + return default + else: + value = override_value + # Coerce the values based on types. + # + # Note: some coercions are only required if we are reading from .ini files, because + # the file format doesn't contain type information, but when reading from toml we will + # get either str or list of str values (see _parse_ini_config_from_pyproject_toml). + # For example: + # + # ini: + # a_line_list = "tests acceptance" + # in this case, we need to split the string to obtain a list of strings. + # + # toml: + # a_line_list = ["tests", "acceptance"] + # in this case, we already have a list ready to use. + # + if type == "paths": + dp = ( + self.inipath.parent + if self.inipath is not None + else self.invocation_params.dir + ) + input_values = shlex.split(value) if isinstance(value, str) else value + return [dp / x for x in input_values] + elif type == "args": + return shlex.split(value) if isinstance(value, str) else value + elif type == "linelist": + if isinstance(value, str): + return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] + else: + return value + elif type == "bool": + return _strtobool(str(value).strip()) + elif type == "string": + return value + elif type == "int": + if not isinstance(value, str): + raise TypeError( + f"Expected an int string for option {name} of type integer, but got: {value!r}" + ) from None + return int(value) + elif type == "float": + if not isinstance(value, str): + raise TypeError( + f"Expected a float string for option {name} of type float, but got: {value!r}" + ) from None + return float(value) + elif type is None: + return value + else: + return self._getini_unknown_type(name, type, value) + + def _getconftest_pathlist( + self, name: str, path: pathlib.Path + ) -> list[pathlib.Path] | None: + try: + mod, relroots = self.pluginmanager._rget_with_confmod(name, path) + except KeyError: + return None + assert mod.__file__ is not None + modpath = pathlib.Path(mod.__file__).parent + values: list[pathlib.Path] = [] + for relroot in relroots: + if isinstance(relroot, os.PathLike): + relroot = pathlib.Path(relroot) + else: + relroot = relroot.replace("/", os.sep) + relroot = absolutepath(modpath / relroot) + values.append(relroot) + return values + + def _get_override_ini_value(self, name: str) -> str | None: + value = None + # override_ini is a list of "ini=value" options. + # Always use the last item if multiple values are set for same ini-name, + # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. + for ini_config in self._override_ini: + try: + key, user_ini_value = ini_config.split("=", 1) + except ValueError as e: + raise UsageError( + f"-o/--override-ini expects option=value style (got: {ini_config!r})." + ) from e + else: + if key == name: + value = user_ini_value + return value + + def getoption(self, name: str, default: Any = notset, skip: bool = False): + """Return command line option value. + + :param name: Name of the option. You may also specify + the literal ``--OPT`` option instead of the "dest" option name. + :param default: Fallback value if no option of that name is **declared** via :hook:`pytest_addoption`. + Note this parameter will be ignored when the option is **declared** even if the option's value is ``None``. + :param skip: If ``True``, raise :func:`pytest.skip` if option is undeclared or has a ``None`` value. + Note that even if ``True``, if a default was specified it will be returned instead of a skip. + """ + name = self._opt2dest.get(name, name) + try: + val = getattr(self.option, name) + if val is None and skip: + raise AttributeError(name) + return val + except AttributeError as e: + if default is not notset: + return default + if skip: + import pytest + + pytest.skip(f"no {name!r} option found") + raise ValueError(f"no option named {name!r}") from e + + def getvalue(self, name: str, path=None): + """Deprecated, use getoption() instead.""" + return self.getoption(name) + + def getvalueorskip(self, name: str, path=None): + """Deprecated, use getoption(skip=True) instead.""" + return self.getoption(name, skip=True) + + #: Verbosity type for failed assertions (see :confval:`verbosity_assertions`). + VERBOSITY_ASSERTIONS: Final = "assertions" + #: Verbosity type for test case execution (see :confval:`verbosity_test_cases`). + VERBOSITY_TEST_CASES: Final = "test_cases" + _VERBOSITY_INI_DEFAULT: Final = "auto" + + def get_verbosity(self, verbosity_type: str | None = None) -> int: + r"""Retrieve the verbosity level for a fine-grained verbosity type. + + :param verbosity_type: Verbosity type to get level for. If a level is + configured for the given type, that value will be returned. If the + given type is not a known verbosity type, the global verbosity + level will be returned. If the given type is None (default), the + global verbosity level will be returned. + + To configure a level for a fine-grained verbosity type, the + configuration file should have a setting for the configuration name + and a numeric value for the verbosity level. A special value of "auto" + can be used to explicitly use the global verbosity level. + + Example: + + .. code-block:: ini + + # content of pytest.ini + [pytest] + verbosity_assertions = 2 + + .. code-block:: console + + pytest -v + + .. code-block:: python + + print(config.get_verbosity()) # 1 + print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 + """ + global_level = self.getoption("verbose", default=0) + assert isinstance(global_level, int) + if verbosity_type is None: + return global_level + + ini_name = Config._verbosity_ini_name(verbosity_type) + if ini_name not in self._parser._inidict: + return global_level + + level = self.getini(ini_name) + if level == Config._VERBOSITY_INI_DEFAULT: + return global_level + + return int(level) + + @staticmethod + def _verbosity_ini_name(verbosity_type: str) -> str: + return f"verbosity_{verbosity_type}" + + @staticmethod + def _add_verbosity_ini(parser: Parser, verbosity_type: str, help: str) -> None: + """Add a output verbosity configuration option for the given output type. + + :param parser: Parser for command line arguments and ini-file values. + :param verbosity_type: Fine-grained verbosity category. + :param help: Description of the output this type controls. + + The value should be retrieved via a call to + :py:func:`config.get_verbosity(type) `. + """ + parser.addini( + Config._verbosity_ini_name(verbosity_type), + help=help, + type="string", + default=Config._VERBOSITY_INI_DEFAULT, + ) + + def _warn_about_missing_assertion(self, mode: str) -> None: + if not _assertion_supported(): + if mode == "plain": + warning_text = ( + "ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?" + ) + else: + warning_text = ( + "assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n" + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), + stacklevel=3, + ) + + def _warn_about_skipped_plugins(self) -> None: + for module_name, msg in self.pluginmanager.skipped_plugins: + self.issue_config_time_warning( + PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), + stacklevel=2, + ) + + +def _assertion_supported() -> bool: + try: + assert False + except AssertionError: + return True + else: + return False # type: ignore[unreachable] + + +def create_terminal_writer( + config: Config, file: TextIO | None = None +) -> TerminalWriter: + """Create a TerminalWriter instance configured according to the options + in the config object. + + Every code which requires a TerminalWriter object and has access to a + config object should use this function. + """ + tw = TerminalWriter(file=file) + + if config.option.color == "yes": + tw.hasmarkup = True + elif config.option.color == "no": + tw.hasmarkup = False + + if config.option.code_highlight == "yes": + tw.code_highlight = True + elif config.option.code_highlight == "no": + tw.code_highlight = False + + return tw + + +def _strtobool(val: str) -> bool: + """Convert a string representation of truth to True or False. + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + + .. note:: Copied from distutils.util. + """ + val = val.lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return True + elif val in ("n", "no", "f", "false", "off", "0"): + return False + else: + raise ValueError(f"invalid truth value {val!r}") + + +@lru_cache(maxsize=50) +def parse_warning_filter( + arg: str, *, escape: bool +) -> tuple[warnings._ActionKind, str, type[Warning], str, int]: + """Parse a warnings filter string. + + This is copied from warnings._setoption with the following changes: + + * Does not apply the filter. + * Escaping is optional. + * Raises UsageError so we get nice error messages on failure. + """ + __tracebackhide__ = True + error_template = dedent( + f"""\ + while parsing the following warning configuration: + + {arg} + + This error occurred: + + {{error}} + """ + ) + + parts = arg.split(":") + if len(parts) > 5: + doc_url = ( + "https://docs.python.org/3/library/warnings.html#describing-warning-filters" + ) + error = dedent( + f"""\ + Too many fields ({len(parts)}), expected at most 5 separated by colons: + + action:message:category:module:line + + For more information please consult: {doc_url} + """ + ) + raise UsageError(error_template.format(error=error)) + + while len(parts) < 5: + parts.append("") + action_, message, category_, module, lineno_ = (s.strip() for s in parts) + try: + action: warnings._ActionKind = warnings._getaction(action_) # type: ignore[attr-defined] + except warnings._OptionError as e: + raise UsageError(error_template.format(error=str(e))) from None + try: + category: type[Warning] = _resolve_warning_category(category_) + except Exception: + exc_info = ExceptionInfo.from_current() + exception_text = exc_info.getrepr(style="native") + raise UsageError(error_template.format(error=exception_text)) from None + if message and escape: + message = re.escape(message) + if module and escape: + module = re.escape(module) + r"\Z" + if lineno_: + try: + lineno = int(lineno_) + if lineno < 0: + raise ValueError("number is negative") + except ValueError as e: + raise UsageError( + error_template.format(error=f"invalid lineno {lineno_!r}: {e}") + ) from None + else: + lineno = 0 + try: + re.compile(message) + re.compile(module) + except re.error as e: + raise UsageError( + error_template.format(error=f"Invalid regex {e.pattern!r}: {e}") + ) from None + return action, message, category, module, lineno + + +def _resolve_warning_category(category: str) -> type[Warning]: + """ + Copied from warnings._getcategory, but changed so it lets exceptions (specially ImportErrors) + propagate so we can get access to their tracebacks (#9218). + """ + __tracebackhide__ = True + if not category: + return Warning + + if "." not in category: + import builtins as m + + klass = category + else: + module, _, klass = category.rpartition(".") + m = __import__(module, None, None, [klass]) + cat = getattr(m, klass) + if not issubclass(cat, Warning): + raise UsageError(f"{cat} is not a Warning subclass") + return cast(type[Warning], cat) + + +def apply_warning_filters( + config_filters: Iterable[str], cmdline_filters: Iterable[str] +) -> None: + """Applies pytest-configured filters to the warnings module""" + # Filters should have this precedence: cmdline options, config. + # Filters should be applied in the inverse order of precedence. + for arg in config_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + for arg in cmdline_filters: + warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) diff --git a/.venv/lib/python3.11/site-packages/_pytest/config/argparsing.py b/.venv/lib/python3.11/site-packages/_pytest/config/argparsing.py new file mode 100644 index 00000000..8d4ed823 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/config/argparsing.py @@ -0,0 +1,535 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Mapping +from collections.abc import Sequence +import os +from typing import Any +from typing import cast +from typing import final +from typing import Literal +from typing import NoReturn + +import _pytest._io +from _pytest.config.exceptions import UsageError +from _pytest.deprecated import check_ispytest + + +FILE_OR_DIR = "file_or_dir" + + +class NotSet: + def __repr__(self) -> str: + return "" + + +NOT_SET = NotSet() + + +@final +class Parser: + """Parser for command line arguments and ini-file values. + + :ivar extra_info: Dict of generic param -> value to display in case + there's an error processing the command line arguments. + """ + + prog: str | None = None + + def __init__( + self, + usage: str | None = None, + processopt: Callable[[Argument], None] | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True) + self._groups: list[OptionGroup] = [] + self._processopt = processopt + self._usage = usage + self._inidict: dict[str, tuple[str, str | None, Any]] = {} + self._ininames: list[str] = [] + self.extra_info: dict[str, Any] = {} + + def processoption(self, option: Argument) -> None: + if self._processopt: + if option.dest: + self._processopt(option) + + def getgroup( + self, name: str, description: str = "", after: str | None = None + ) -> OptionGroup: + """Get (or create) a named option Group. + + :param name: Name of the option group. + :param description: Long description for --help output. + :param after: Name of another group, used for ordering --help output. + :returns: The option group. + + The returned group object has an ``addoption`` method with the same + signature as :func:`parser.addoption ` but + will be shown in the respective group in the output of + ``pytest --help``. + """ + for group in self._groups: + if group.name == name: + return group + group = OptionGroup(name, description, parser=self, _ispytest=True) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i + 1, group) + return group + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Register a command line option. + + :param opts: + Option names, can be short or long options. + :param attrs: + Same attributes as the argparse library's :meth:`add_argument() + ` function accepts. + + After command line parsing, options are available on the pytest config + object via ``config.option.NAME`` where ``NAME`` is usually set + by passing a ``dest`` attribute, for example + ``addoption("--long", dest="NAME", ...)``. + """ + self._anonymous.addoption(*opts, **attrs) + + def parse( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> argparse.Namespace: + from _pytest._argcomplete import try_argcomplete + + self.optparser = self._getparser() + try_argcomplete(self.optparser) + strargs = [os.fspath(x) for x in args] + return self.optparser.parse_args(strargs, namespace=namespace) + + def _getparser(self) -> MyOptionParser: + from _pytest._argcomplete import filescompleter + + optparser = MyOptionParser(self, self.extra_info, prog=self.prog) + groups = [*self._groups, self._anonymous] + for group in groups: + if group.options: + desc = group.description or group.name + arggroup = optparser.add_argument_group(desc) + for option in group.options: + n = option.names() + a = option.attrs() + arggroup.add_argument(*n, **a) + file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") + # bash like autocompletion for dirs (appending '/') + # Type ignored because typeshed doesn't know about argcomplete. + file_or_dir_arg.completer = filescompleter # type: ignore + return optparser + + def parse_setoption( + self, + args: Sequence[str | os.PathLike[str]], + option: argparse.Namespace, + namespace: argparse.Namespace | None = None, + ) -> list[str]: + parsedoption = self.parse(args, namespace=namespace) + for name, value in parsedoption.__dict__.items(): + setattr(option, name, value) + return cast(list[str], getattr(parsedoption, FILE_OR_DIR)) + + def parse_known_args( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> argparse.Namespace: + """Parse the known arguments at this point. + + :returns: An argparse namespace object. + """ + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] + + def parse_known_and_unknown_args( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> tuple[argparse.Namespace, list[str]]: + """Parse the known arguments at this point, and also return the + remaining unknown arguments. + + :returns: + A tuple containing an argparse namespace object for the known + arguments, and a list of the unknown arguments. + """ + optparser = self._getparser() + strargs = [os.fspath(x) for x in args] + return optparser.parse_known_args(strargs, namespace=namespace) + + def addini( + self, + name: str, + help: str, + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ] + | None = None, + default: Any = NOT_SET, + ) -> None: + """Register an ini-file option. + + :param name: + Name of the ini-variable. + :param type: + Type of the variable. Can be: + + * ``string``: a string + * ``bool``: a boolean + * ``args``: a list of strings, separated as in a shell + * ``linelist``: a list of strings, separated by line breaks + * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell + * ``pathlist``: a list of ``py.path``, separated as in a shell + * ``int``: an integer + * ``float``: a floating-point number + + .. versionadded:: 8.4 + + The ``float`` and ``int`` types. + + For ``paths`` and ``pathlist`` types, they are considered relative to the ini-file. + In case the execution is happening without an ini-file defined, + they will be considered relative to the current working directory (for example with ``--override-ini``). + + .. versionadded:: 7.0 + The ``paths`` variable type. + + .. versionadded:: 8.1 + Use the current working directory to resolve ``paths`` and ``pathlist`` in the absence of an ini-file. + + Defaults to ``string`` if ``None`` or not passed. + :param default: + Default value if no ini-file option exists but is queried. + + The value of ini-variables can be retrieved via a call to + :py:func:`config.getini(name) `. + """ + assert type in ( + None, + "string", + "paths", + "pathlist", + "args", + "linelist", + "bool", + "int", + "float", + ) + if default is NOT_SET: + default = get_ini_default_for_type(type) + + self._inidict[name] = (help, type, default) + self._ininames.append(name) + + +def get_ini_default_for_type( + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ] + | None, +) -> Any: + """ + Used by addini to get the default value for a given ini-option type, when + default is not supplied. + """ + if type is None: + return "" + elif type in ("paths", "pathlist", "args", "linelist"): + return [] + elif type == "bool": + return False + elif type == "int": + return 0 + elif type == "float": + return 0.0 + else: + return "" + + +class ArgumentError(Exception): + """Raised if an Argument instance is created with invalid or + inconsistent arguments.""" + + def __init__(self, msg: str, option: Argument | str) -> None: + self.msg = msg + self.option_id = str(option) + + def __str__(self) -> str: + if self.option_id: + return f"option {self.option_id}: {self.msg}" + else: + return self.msg + + +class Argument: + """Class that mimics the necessary behaviour of optparse.Option. + + It's currently a least effort implementation and ignoring choices + and integer prefixes. + + https://docs.python.org/3/library/optparse.html#optparse-standard-option-types + """ + + def __init__(self, *names: str, **attrs: Any) -> None: + """Store params in private vars for use in add_argument.""" + self._attrs = attrs + self._short_opts: list[str] = [] + self._long_opts: list[str] = [] + try: + self.type = attrs["type"] + except KeyError: + pass + try: + # Attribute existence is tested in Config._processopt. + self.default = attrs["default"] + except KeyError: + pass + self._set_opt_strings(names) + dest: str | None = attrs.get("dest") + if dest: + self.dest = dest + elif self._long_opts: + self.dest = self._long_opts[0][2:].replace("-", "_") + else: + try: + self.dest = self._short_opts[0][1:] + except IndexError as e: + self.dest = "???" # Needed for the error repr. + raise ArgumentError("need a long or short option", self) from e + + def names(self) -> list[str]: + return self._short_opts + self._long_opts + + def attrs(self) -> Mapping[str, Any]: + # Update any attributes set by processopt. + attrs = "default dest help".split() + attrs.append(self.dest) + for attr in attrs: + try: + self._attrs[attr] = getattr(self, attr) + except AttributeError: + pass + return self._attrs + + def _set_opt_strings(self, opts: Sequence[str]) -> None: + """Directly from optparse. + + Might not be necessary as this is passed to argparse later on. + """ + for opt in opts: + if len(opt) < 2: + raise ArgumentError( + f"invalid option string {opt!r}: " + "must be at least two characters long", + self, + ) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise ArgumentError( + f"invalid short option string {opt!r}: " + "must be of the form -x, (x any non-dash char)", + self, + ) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise ArgumentError( + f"invalid long option string {opt!r}: " + "must start with --, followed by non-dash", + self, + ) + self._long_opts.append(opt) + + def __repr__(self) -> str: + args: list[str] = [] + if self._short_opts: + args += ["_short_opts: " + repr(self._short_opts)] + if self._long_opts: + args += ["_long_opts: " + repr(self._long_opts)] + args += ["dest: " + repr(self.dest)] + if hasattr(self, "type"): + args += ["type: " + repr(self.type)] + if hasattr(self, "default"): + args += ["default: " + repr(self.default)] + return "Argument({})".format(", ".join(args)) + + +class OptionGroup: + """A group of options shown in its own section.""" + + def __init__( + self, + name: str, + description: str = "", + parser: Parser | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.name = name + self.description = description + self.options: list[Argument] = [] + self.parser = parser + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Add an option to this group. + + If a shortened version of a long option is specified, it will + be suppressed in the help. ``addoption('--twowords', '--two-words')`` + results in help showing ``--two-words`` only, but ``--twowords`` gets + accepted **and** the automatic destination is in ``args.twowords``. + + :param opts: + Option names, can be short or long options. + :param attrs: + Same attributes as the argparse library's :meth:`add_argument() + ` function accepts. + """ + conflict = set(opts).intersection( + name for opt in self.options for name in opt.names() + ) + if conflict: + raise ValueError(f"option names {conflict} already added") + option = Argument(*opts, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *opts: str, **attrs: Any) -> None: + option = Argument(*opts, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option: Argument, shortupper: bool = False) -> None: + if not shortupper: + for opt in option._short_opts: + if opt[0] == "-" and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") + if self.parser: + self.parser.processoption(option) + self.options.append(option) + + +class MyOptionParser(argparse.ArgumentParser): + def __init__( + self, + parser: Parser, + extra_info: dict[str, Any] | None = None, + prog: str | None = None, + ) -> None: + self._parser = parser + super().__init__( + prog=prog, + usage=parser._usage, + add_help=False, + formatter_class=DropShorterLongHelpFormatter, + allow_abbrev=False, + fromfile_prefix_chars="@", + ) + # extra_info is a dict of (param -> value) to display if there's + # an usage error to provide more contextual information to the user. + self.extra_info = extra_info if extra_info else {} + + def error(self, message: str) -> NoReturn: + """Transform argparse error message into UsageError.""" + msg = f"{self.prog}: error: {message}" + + if hasattr(self._parser, "_config_source_hint"): + msg = f"{msg} ({self._parser._config_source_hint})" + + raise UsageError(self.format_usage() + msg) + + # Type ignored because typeshed has a very complex type in the superclass. + def parse_args( # type: ignore + self, + args: Sequence[str] | None = None, + namespace: argparse.Namespace | None = None, + ) -> argparse.Namespace: + """Allow splitting of positional arguments.""" + parsed, unrecognized = self.parse_known_args(args, namespace) + if unrecognized: + for arg in unrecognized: + if arg and arg[0] == "-": + lines = [ + "unrecognized arguments: {}".format(" ".join(unrecognized)) + ] + for k, v in sorted(self.extra_info.items()): + lines.append(f" {k}: {v}") + self.error("\n".join(lines)) + getattr(parsed, FILE_OR_DIR).extend(unrecognized) + return parsed + + +class DropShorterLongHelpFormatter(argparse.HelpFormatter): + """Shorten help for long options that differ only in extra hyphens. + + - Collapse **long** options that are the same except for extra hyphens. + - Shortcut if there are only two options and one of them is a short one. + - Cache result on the action object as this is called at least 2 times. + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # Use more accurate terminal width. + if "width" not in kwargs: + kwargs["width"] = _pytest._io.get_terminal_width() + super().__init__(*args, **kwargs) + + def _format_action_invocation(self, action: argparse.Action) -> str: + orgstr = super()._format_action_invocation(action) + if orgstr and orgstr[0] != "-": # only optional arguments + return orgstr + res: str | None = getattr(action, "_formatted_action_invocation", None) + if res: + return res + options = orgstr.split(", ") + if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): + # a shortcut for '-h, --help' or '--abc', '-a' + action._formatted_action_invocation = orgstr # type: ignore + return orgstr + return_list = [] + short_long: dict[str, str] = {} + for option in options: + if len(option) == 2 or option[2] == " ": + continue + if not option.startswith("--"): + raise ArgumentError( + f'long optional argument without "--": [{option}]', option + ) + xxoption = option[2:] + shortened = xxoption.replace("-", "") + if shortened not in short_long or len(short_long[shortened]) < len( + xxoption + ): + short_long[shortened] = xxoption + # now short_long has been filled out to the longest with dashes + # **and** we keep the right option ordering from add_argument + for option in options: + if len(option) == 2 or option[2] == " ": + return_list.append(option) + if option[2:] == short_long.get(option.replace("-", "")): + return_list.append(option.replace(" ", "=", 1)) + formatted_action_invocation = ", ".join(return_list) + action._formatted_action_invocation = formatted_action_invocation # type: ignore + return formatted_action_invocation + + def _split_lines(self, text, width): + """Wrap lines after splitting on original newlines. + + This allows to have explicit line breaks in the help text. + """ + import textwrap + + lines = [] + for line in text.splitlines(): + lines.extend(textwrap.wrap(line.strip(), width)) + return lines diff --git a/.venv/lib/python3.11/site-packages/_pytest/config/compat.py b/.venv/lib/python3.11/site-packages/_pytest/config/compat.py new file mode 100644 index 00000000..21eab4c7 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/config/compat.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from collections.abc import Mapping +import functools +from pathlib import Path +from typing import Any +import warnings + +import pluggy + +from ..compat import LEGACY_PATH +from ..compat import legacy_path +from ..deprecated import HOOK_LEGACY_PATH_ARG + + +# hookname: (Path, LEGACY_PATH) +imply_paths_hooks: Mapping[str, tuple[str, str]] = { + "pytest_ignore_collect": ("collection_path", "path"), + "pytest_collect_file": ("file_path", "path"), + "pytest_pycollect_makemodule": ("module_path", "path"), + "pytest_report_header": ("start_path", "startdir"), + "pytest_report_collectionfinish": ("start_path", "startdir"), +} + + +def _check_path(path: Path, fspath: LEGACY_PATH) -> None: + if Path(fspath) != path: + raise ValueError( + f"Path({fspath!r}) != {path!r}\n" + "if both path and fspath are given they need to be equal" + ) + + +class PathAwareHookProxy: + """ + this helper wraps around hook callers + until pluggy supports fixingcalls, this one will do + + it currently doesn't return full hook caller proxies for fixed hooks, + this may have to be changed later depending on bugs + """ + + def __init__(self, hook_relay: pluggy.HookRelay) -> None: + self._hook_relay = hook_relay + + def __dir__(self) -> list[str]: + return dir(self._hook_relay) + + def __getattr__(self, key: str) -> pluggy.HookCaller: + hook: pluggy.HookCaller = getattr(self._hook_relay, key) + if key not in imply_paths_hooks: + self.__dict__[key] = hook + return hook + else: + path_var, fspath_var = imply_paths_hooks[key] + + @functools.wraps(hook) + def fixed_hook(**kw: Any) -> Any: + path_value: Path | None = kw.pop(path_var, None) + fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None) + if fspath_value is not None: + warnings.warn( + HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg=fspath_var, pathlib_path_arg=path_var + ), + stacklevel=2, + ) + if path_value is not None: + if fspath_value is not None: + _check_path(path_value, fspath_value) + else: + fspath_value = legacy_path(path_value) + else: + assert fspath_value is not None + path_value = Path(fspath_value) + + kw[path_var] = path_value + kw[fspath_var] = fspath_value + return hook(**kw) + + fixed_hook.name = hook.name # type: ignore[attr-defined] + fixed_hook.spec = hook.spec # type: ignore[attr-defined] + fixed_hook.__name__ = key + self.__dict__[key] = fixed_hook + return fixed_hook # type: ignore[return-value] diff --git a/.venv/lib/python3.11/site-packages/_pytest/config/exceptions.py b/.venv/lib/python3.11/site-packages/_pytest/config/exceptions.py new file mode 100644 index 00000000..90108eca --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/config/exceptions.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing import final + + +@final +class UsageError(Exception): + """Error in pytest usage or invocation.""" + + +class PrintHelp(Exception): + """Raised when pytest should print its help to skip the rest of the + argument parsing and validation.""" diff --git a/.venv/lib/python3.11/site-packages/_pytest/config/findpaths.py b/.venv/lib/python3.11/site-packages/_pytest/config/findpaths.py new file mode 100644 index 00000000..15bfbb06 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/config/findpaths.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +from collections.abc import Iterable +from collections.abc import Sequence +import os +from pathlib import Path +import sys +from typing import TYPE_CHECKING + +import iniconfig + +from .exceptions import UsageError +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.pathlib import commonpath +from _pytest.pathlib import safe_exists + + +if TYPE_CHECKING: + from typing import Union + + from typing_extensions import TypeAlias + + # Even though TOML supports richer data types, all values are converted to str/list[str] during + # parsing to maintain compatibility with the rest of the configuration system. + ConfigDict: TypeAlias = dict[str, Union[str, list[str]]] + + +def _parse_ini_config(path: Path) -> iniconfig.IniConfig: + """Parse the given generic '.ini' file using legacy IniConfig parser, returning + the parsed object. + + Raise UsageError if the file cannot be parsed. + """ + try: + return iniconfig.IniConfig(str(path)) + except iniconfig.ParseError as exc: + raise UsageError(str(exc)) from exc + + +def load_config_dict_from_file( + filepath: Path, +) -> ConfigDict | None: + """Load pytest configuration from the given file path, if supported. + + Return None if the file does not contain valid pytest configuration. + """ + # Configuration from ini files are obtained from the [pytest] section, if present. + if filepath.suffix == ".ini": + iniconfig = _parse_ini_config(filepath) + + if "pytest" in iniconfig: + return dict(iniconfig["pytest"].items()) + else: + # "pytest.ini" files are always the source of configuration, even if empty. + if filepath.name == "pytest.ini": + return {} + + # '.cfg' files are considered if they contain a "[tool:pytest]" section. + elif filepath.suffix == ".cfg": + iniconfig = _parse_ini_config(filepath) + + if "tool:pytest" in iniconfig.sections: + return dict(iniconfig["tool:pytest"].items()) + elif "pytest" in iniconfig.sections: + # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that + # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). + fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) + + # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. + elif filepath.suffix == ".toml": + if sys.version_info >= (3, 11): + import tomllib + else: + import tomli as tomllib + + toml_text = filepath.read_text(encoding="utf-8") + try: + config = tomllib.loads(toml_text) + except tomllib.TOMLDecodeError as exc: + raise UsageError(f"{filepath}: {exc}") from exc + + result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) + if result is not None: + # TOML supports richer data types than ini files (strings, arrays, floats, ints, etc), + # however we need to convert all scalar values to str for compatibility with the rest + # of the configuration system, which expects strings only. + def make_scalar(v: object) -> str | list[str]: + return v if isinstance(v, list) else str(v) + + return {k: make_scalar(v) for k, v in result.items()} + + return None + + +def locate_config( + invocation_dir: Path, + args: Iterable[Path], +) -> tuple[Path | None, Path | None, ConfigDict]: + """Search in the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict).""" + config_names = [ + "pytest.ini", + ".pytest.ini", + "pyproject.toml", + "tox.ini", + "setup.cfg", + ] + args = [x for x in args if not str(x).startswith("-")] + if not args: + args = [invocation_dir] + found_pyproject_toml: Path | None = None + for arg in args: + argpath = absolutepath(arg) + for base in (argpath, *argpath.parents): + for config_name in config_names: + p = base / config_name + if p.is_file(): + if p.name == "pyproject.toml" and found_pyproject_toml is None: + found_pyproject_toml = p + ini_config = load_config_dict_from_file(p) + if ini_config is not None: + return base, p, ini_config + if found_pyproject_toml is not None: + return found_pyproject_toml.parent, found_pyproject_toml, {} + return None, None, {} + + +def get_common_ancestor( + invocation_dir: Path, + paths: Iterable[Path], +) -> Path: + common_ancestor: Path | None = None + for path in paths: + if not path.exists(): + continue + if common_ancestor is None: + common_ancestor = path + else: + if common_ancestor in path.parents or path == common_ancestor: + continue + elif path in common_ancestor.parents: + common_ancestor = path + else: + shared = commonpath(path, common_ancestor) + if shared is not None: + common_ancestor = shared + if common_ancestor is None: + common_ancestor = invocation_dir + elif common_ancestor.is_file(): + common_ancestor = common_ancestor.parent + return common_ancestor + + +def get_dirs_from_args(args: Iterable[str]) -> list[Path]: + def is_option(x: str) -> bool: + return x.startswith("-") + + def get_file_part_from_node_id(x: str) -> str: + return x.split("::")[0] + + def get_dir_from_path(path: Path) -> Path: + if path.is_dir(): + return path + return path.parent + + # These look like paths but may not exist + possible_paths = ( + absolutepath(get_file_part_from_node_id(arg)) + for arg in args + if not is_option(arg) + ) + + return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] + + +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + +def determine_setup( + *, + inifile: str | None, + args: Sequence[str], + rootdir_cmd_arg: str | None, + invocation_dir: Path, +) -> tuple[Path, Path | None, ConfigDict]: + """Determine the rootdir, inifile and ini configuration values from the + command line arguments. + + :param inifile: + The `--inifile` command line argument, if given. + :param args: + The free command line arguments. + :param rootdir_cmd_arg: + The `--rootdir` command line argument, if given. + :param invocation_dir: + The working directory when pytest was invoked. + """ + rootdir = None + dirs = get_dirs_from_args(args) + if inifile: + inipath_ = absolutepath(inifile) + inipath: Path | None = inipath_ + inicfg = load_config_dict_from_file(inipath_) or {} + if rootdir_cmd_arg is None: + rootdir = inipath_.parent + else: + ancestor = get_common_ancestor(invocation_dir, dirs) + rootdir, inipath, inicfg = locate_config(invocation_dir, [ancestor]) + if rootdir is None and rootdir_cmd_arg is None: + for possible_rootdir in (ancestor, *ancestor.parents): + if (possible_rootdir / "setup.py").is_file(): + rootdir = possible_rootdir + break + else: + if dirs != [ancestor]: + rootdir, inipath, inicfg = locate_config(invocation_dir, dirs) + if rootdir is None: + rootdir = get_common_ancestor( + invocation_dir, [invocation_dir, ancestor] + ) + if is_fs_root(rootdir): + rootdir = ancestor + if rootdir_cmd_arg: + rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) + if not rootdir.is_dir(): + raise UsageError( + f"Directory '{rootdir}' not found. Check your '--rootdir' option." + ) + assert rootdir is not None + return rootdir, inipath, inicfg or {} + + +def is_fs_root(p: Path) -> bool: + r""" + Return True if the given path is pointing to the root of the + file system ("/" on Unix and "C:\\" on Windows for example). + """ + return os.path.splitdrive(str(p))[1] == os.sep diff --git a/.venv/lib/python3.11/site-packages/_pytest/debugging.py b/.venv/lib/python3.11/site-packages/_pytest/debugging.py new file mode 100644 index 00000000..de1b2688 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/debugging.py @@ -0,0 +1,407 @@ +# mypy: allow-untyped-defs +# ruff: noqa: T100 +"""Interactive debugging with PDB, the Python Debugger.""" + +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Generator +import functools +import sys +import types +from typing import Any +import unittest + +from _pytest import outcomes +from _pytest._code import ExceptionInfo +from _pytest.capture import CaptureManager +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.config.exceptions import UsageError +from _pytest.nodes import Node +from _pytest.reports import BaseReport +from _pytest.runner import CallInfo + + +def _validate_usepdb_cls(value: str) -> tuple[str, str]: + """Validate syntax of --pdbcls option.""" + try: + modname, classname = value.split(":") + except ValueError as e: + raise argparse.ArgumentTypeError( + f"{value!r} is not in the format 'modname:classname'" + ) from e + return (modname, classname) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--pdb", + dest="usepdb", + action="store_true", + help="Start the interactive Python debugger on errors or KeyboardInterrupt", + ) + group.addoption( + "--pdbcls", + dest="usepdb_cls", + metavar="modulename:classname", + type=_validate_usepdb_cls, + help="Specify a custom interactive Python debugger for use with --pdb." + "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", + ) + group.addoption( + "--trace", + dest="trace", + action="store_true", + help="Immediately break when running each test", + ) + + +def pytest_configure(config: Config) -> None: + import pdb + + if config.getvalue("trace"): + config.pluginmanager.register(PdbTrace(), "pdbtrace") + if config.getvalue("usepdb"): + config.pluginmanager.register(PdbInvoke(), "pdbinvoke") + + pytestPDB._saved.append( + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + ) + pdb.set_trace = pytestPDB.set_trace + pytestPDB._pluginmanager = config.pluginmanager + pytestPDB._config = config + + # NOTE: not using pytest_unconfigure, since it might get called although + # pytest_configure was not (if another plugin raises UsageError). + def fin() -> None: + ( + pdb.set_trace, + pytestPDB._pluginmanager, + pytestPDB._config, + ) = pytestPDB._saved.pop() + + config.add_cleanup(fin) + + +class pytestPDB: + """Pseudo PDB that defers to the real pdb.""" + + _pluginmanager: PytestPluginManager | None = None + _config: Config | None = None + _saved: list[ + tuple[Callable[..., None], PytestPluginManager | None, Config | None] + ] = [] + _recursive_debug = 0 + _wrapped_pdb_cls: tuple[type[Any], type[Any]] | None = None + + @classmethod + def _is_capturing(cls, capman: CaptureManager | None) -> str | bool: + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman: CaptureManager | None): + if not cls._config: + import pdb + + # Happens when using pytest.set_trace outside of a test. + return pdb.Pdb + + usepdb_cls = cls._config.getvalue("usepdb_cls") + + if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: + return cls._wrapped_pdb_cls[1] + + if usepdb_cls: + modname, classname = usepdb_cls + + try: + __import__(modname) + mod = sys.modules[modname] + + # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). + parts = classname.split(".") + pdb_cls = getattr(mod, parts[0]) + for part in parts[1:]: + pdb_cls = getattr(pdb_cls, part) + except Exception as exc: + value = ":".join((modname, classname)) + raise UsageError( + f"--pdbcls: could not import {value!r}: {exc}" + ) from exc + else: + import pdb + + pdb_cls = pdb.Pdb + + wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) + cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) + return wrapped_cls + + @classmethod + def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None): + import _pytest.config + + class PytestPdbWrapper(pdb_cls): + _pytest_capman = capman + _continued = False + + def do_debug(self, arg): + cls._recursive_debug += 1 + ret = super().do_debug(arg) + cls._recursive_debug -= 1 + return ret + + if hasattr(pdb_cls, "do_debug"): + do_debug.__doc__ = pdb_cls.do_debug.__doc__ + + def do_continue(self, arg): + ret = super().do_continue(arg) + if cls._recursive_debug == 0: + assert cls._config is not None + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + capman = self._pytest_capman + capturing = pytestPDB._is_capturing(capman) + if capturing: + if capturing == "global": + tw.sep(">", "PDB continue (IO-capturing resumed)") + else: + tw.sep( + ">", + f"PDB continue (IO-capturing resumed for {capturing})", + ) + assert capman is not None + capman.resume() + else: + tw.sep(">", "PDB continue") + assert cls._pluginmanager is not None + cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) + self._continued = True + return ret + + if hasattr(pdb_cls, "do_continue"): + do_continue.__doc__ = pdb_cls.do_continue.__doc__ + + do_c = do_cont = do_continue + + def do_quit(self, arg): + # Raise Exit outcome when quit command is used in pdb. + # + # This is a bit of a hack - it would be better if BdbQuit + # could be handled, but this would require to wrap the + # whole pytest run, and adjust the report etc. + ret = super().do_quit(arg) + + if cls._recursive_debug == 0: + outcomes.exit("Quitting debugger") + + return ret + + if hasattr(pdb_cls, "do_quit"): + do_quit.__doc__ = pdb_cls.do_quit.__doc__ + + do_q = do_quit + do_exit = do_quit + + def setup(self, f, tb): + """Suspend on setup(). + + Needed after do_continue resumed, and entering another + breakpoint again. + """ + ret = super().setup(f, tb) + if not ret and self._continued: + # pdb.setup() returns True if the command wants to exit + # from the interaction: do not suspend capturing then. + if self._pytest_capman: + self._pytest_capman.suspend_global_capture(in_=True) + return ret + + def get_stack(self, f, t): + stack, i = super().get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return stack, i + + return PytestPdbWrapper + + @classmethod + def _init_pdb(cls, method, *args, **kwargs): + """Initialize PDB debugging, dropping any IO capturing.""" + import _pytest.config + + if cls._pluginmanager is None: + capman: CaptureManager | None = None + else: + capman = cls._pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend(in_=True) + + if cls._config: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + if cls._recursive_debug == 0: + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + else: + capturing = cls._is_capturing(capman) + if capturing == "global": + tw.sep(">", f"PDB {method} (IO-capturing turned off)") + elif capturing: + tw.sep( + ">", + f"PDB {method} (IO-capturing turned off for {capturing})", + ) + else: + tw.sep(">", f"PDB {method}") + + _pdb = cls._import_pdb_cls(capman)(**kwargs) + + if cls._pluginmanager: + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) + return _pdb + + @classmethod + def set_trace(cls, *args, **kwargs) -> None: + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + frame = sys._getframe().f_back + _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb.set_trace(frame) + + +class PdbInvoke: + def pytest_exception_interact( + self, node: Node, call: CallInfo[Any], report: BaseReport + ) -> None: + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stdout.write(err) + assert call.excinfo is not None + + if not isinstance(call.excinfo.value, unittest.SkipTest): + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: + exc_or_tb = _postmortem_exc_or_tb(excinfo) + post_mortem(exc_or_tb) + + +class PdbTrace: + @hookimpl(wrapper=True) + def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, object, object]: + wrap_pytest_function_for_tracing(pyfuncitem) + return (yield) + + +def wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Change the Python function object of the given Function item by a + wrapper which actually enters pdb before calling the python function + itself, effectively leaving the user in the pdb prompt in the first + statement of the function.""" + _pdb = pytestPDB._init_pdb("runcall") + testfunction = pyfuncitem.obj + + # we can't just return `partial(pdb.runcall, testfunction)` because (on + # python < 3.7.4) runcall's first param is `func`, which means we'd get + # an exception if one of the kwargs to testfunction was called `func`. + @functools.wraps(testfunction) + def wrapper(*args, **kwargs) -> None: + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper + + +def maybe_wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Wrap the given pytestfunct item for tracing support if --trace was given in + the command line.""" + if pyfuncitem.config.getvalue("trace"): + wrap_pytest_function_for_tracing(pyfuncitem) + + +def _enter_pdb( + node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport +) -> BaseReport: + # XXX we reuse the TerminalReporter's terminalwriter + # because this seems to avoid some encoding related troubles + # for not completely clear reasons. + tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw.line() + + showcapture = node.config.option.showcapture + + for sectionname, content in ( + ("stdout", rep.capstdout), + ("stderr", rep.capstderr), + ("log", rep.caplog), + ): + if showcapture in (sectionname, "all") and content: + tw.sep(">", "captured " + sectionname) + if content[-1:] == "\n": + content = content[:-1] + tw.line(content) + + tw.sep(">", "traceback") + rep.toterminal(tw) + tw.sep(">", "entering PDB") + tb_or_exc = _postmortem_exc_or_tb(excinfo) + rep._pdbshown = True # type: ignore[attr-defined] + post_mortem(tb_or_exc) + return rep + + +def _postmortem_exc_or_tb( + excinfo: ExceptionInfo[BaseException], +) -> types.TracebackType | BaseException: + from doctest import UnexpectedException + + get_exc = sys.version_info >= (3, 13) + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + underlying_exc = excinfo.value + if get_exc: + return underlying_exc.exc_info[1] + + return underlying_exc.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + cause = excinfo.value.cause + if get_exc: + return cause + + assert cause.__traceback__ is not None + return cause.__traceback__ + else: + assert excinfo._excinfo is not None + if get_exc: + return excinfo._excinfo[1] + + return excinfo._excinfo[2] + + +def post_mortem(tb_or_exc: types.TracebackType | BaseException) -> None: + p = pytestPDB._init_pdb("post_mortem") + p.reset() + p.interaction(None, tb_or_exc) + if p.quitting: + outcomes.exit("Quitting debugger") diff --git a/.venv/lib/python3.11/site-packages/_pytest/deprecated.py b/.venv/lib/python3.11/site-packages/_pytest/deprecated.py new file mode 100644 index 00000000..a605c24e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/deprecated.py @@ -0,0 +1,91 @@ +"""Deprecation messages and bits of code used elsewhere in the codebase that +is planned to be removed in the next pytest release. + +Keeping it in a central location makes it easy to track what is deprecated and should +be removed when the time comes. + +All constants defined in this module should be either instances of +:class:`PytestWarning`, or :class:`UnformattedWarning` +in case of warnings which need to format their messages. +""" + +from __future__ import annotations + +from warnings import warn + +from _pytest.warning_types import PytestDeprecationWarning +from _pytest.warning_types import PytestRemovedIn9Warning +from _pytest.warning_types import UnformattedWarning + + +# set of plugins which have been integrated into the core; we use this list to ignore +# them during registration to avoid conflicts +DEPRECATED_EXTERNAL_PLUGINS = { + "pytest_catchlog", + "pytest_capturelog", + "pytest_faulthandler", +} + + +# This can be* removed pytest 8, but it's harmless and common, so no rush to remove. +# * If you're in the future: "could have been". +YIELD_FIXTURE = PytestDeprecationWarning( + "@pytest.yield_fixture is deprecated.\n" + "Use @pytest.fixture instead; they are the same." +) + +# This deprecation is never really meant to be removed. +PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") + + +HOOK_LEGACY_PATH_ARG = UnformattedWarning( + PytestRemovedIn9Warning, + "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" + "see https://docs.pytest.org/en/latest/deprecations.html" + "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", +) + +NODE_CTOR_FSPATH_ARG = UnformattedWarning( + PytestRemovedIn9Warning, + "The (fspath: py.path.local) argument to {node_type_name} is deprecated. " + "Please use the (path: pathlib.Path) argument instead.\n" + "See https://docs.pytest.org/en/latest/deprecations.html" + "#fspath-argument-for-node-constructors-replaced-with-pathlib-path", +) + +HOOK_LEGACY_MARKING = UnformattedWarning( + PytestDeprecationWarning, + "The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n" + "Please use the pytest.hook{type}({hook_opts}) decorator instead\n" + " to configure the hooks.\n" + " See https://docs.pytest.org/en/latest/deprecations.html" + "#configuring-hook-specs-impls-using-markers", +) + +MARKED_FIXTURE = PytestRemovedIn9Warning( + "Marks applied to fixtures have no effect\n" + "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" +) + +# You want to make some `__init__` or function "private". +# +# def my_private_function(some, args): +# ... +# +# Do this: +# +# def my_private_function(some, args, *, _ispytest: bool = False): +# check_ispytest(_ispytest) +# ... +# +# Change all internal/allowed calls to +# +# my_private_function(some, args, _ispytest=True) +# +# All other calls will get the default _ispytest=False and trigger +# the warning (possibly error in the future). + + +def check_ispytest(ispytest: bool) -> None: + if not ispytest: + warn(PRIVATE, stacklevel=3) diff --git a/.venv/lib/python3.11/site-packages/_pytest/doctest.py b/.venv/lib/python3.11/site-packages/_pytest/doctest.py new file mode 100644 index 00000000..0dbef605 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/doctest.py @@ -0,0 +1,754 @@ +# mypy: allow-untyped-defs +"""Discover and run doctests in modules and test files.""" + +from __future__ import annotations + +import bdb +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence +from contextlib import contextmanager +import functools +import inspect +import os +from pathlib import Path +import platform +import re +import sys +import traceback +import types +from typing import Any +from typing import TYPE_CHECKING +import warnings + +from _pytest import outcomes +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import safe_getattr +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.fixtures import fixture +from _pytest.fixtures import TopRequest +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import OutcomeException +from _pytest.outcomes import skip +from _pytest.pathlib import fnmatch_ex +from _pytest.python import Module +from _pytest.python_api import approx +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + import doctest + + from typing_extensions import Self + +DOCTEST_REPORT_CHOICE_NONE = "none" +DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" +DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" +DOCTEST_REPORT_CHOICE_UDIFF = "udiff" +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure" + +DOCTEST_REPORT_CHOICES = ( + DOCTEST_REPORT_CHOICE_NONE, + DOCTEST_REPORT_CHOICE_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF, + DOCTEST_REPORT_CHOICE_UDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, +) + +# Lazy definition of runner class +RUNNER_CLASS = None +# Lazy definition of output checker class +CHECKER_CLASS: type[doctest.OutputChecker] | None = None + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "doctest_optionflags", + "Option flags for doctests", + type="args", + default=["ELLIPSIS"], + ) + parser.addini( + "doctest_encoding", "Encoding used for doctest files", default="utf-8" + ) + group = parser.getgroup("collect") + group.addoption( + "--doctest-modules", + action="store_true", + default=False, + help="Run doctests in all .py modules", + dest="doctestmodules", + ) + group.addoption( + "--doctest-report", + type=str.lower, + default="udiff", + help="Choose another output format for diffs on doctest failure", + choices=DOCTEST_REPORT_CHOICES, + dest="doctestreport", + ) + group.addoption( + "--doctest-glob", + action="append", + default=[], + metavar="pat", + help="Doctests file matching pattern, default: test*.txt", + dest="doctestglob", + ) + group.addoption( + "--doctest-ignore-import-errors", + action="store_true", + default=False, + help="Ignore doctest collection errors", + dest="doctest_ignore_import_errors", + ) + group.addoption( + "--doctest-continue-on-failure", + action="store_true", + default=False, + help="For a given doctest, continue to run after the first failure", + dest="doctest_continue_on_failure", + ) + + +def pytest_unconfigure() -> None: + global RUNNER_CLASS + + RUNNER_CLASS = None + + +def pytest_collect_file( + file_path: Path, + parent: Collector, +) -> DoctestModule | DoctestTextfile | None: + config = parent.config + if file_path.suffix == ".py": + if config.option.doctestmodules and not any( + (_is_setup_py(file_path), _is_main_py(file_path)) + ): + return DoctestModule.from_parent(parent, path=file_path) + elif _is_doctest(config, file_path, parent): + return DoctestTextfile.from_parent(parent, path=file_path) + return None + + +def _is_setup_py(path: Path) -> bool: + if path.name != "setup.py": + return False + contents = path.read_bytes() + return b"setuptools" in contents or b"distutils" in contents + + +def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: + if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): + return True + globs = config.getoption("doctestglob") or ["test*.txt"] + return any(fnmatch_ex(glob, path) for glob in globs) + + +def _is_main_py(path: Path) -> bool: + return path.name == "__main__.py" + + +class ReprFailDoctest(TerminalRepr): + def __init__( + self, reprlocation_lines: Sequence[tuple[ReprFileLocation, Sequence[str]]] + ) -> None: + self.reprlocation_lines = reprlocation_lines + + def toterminal(self, tw: TerminalWriter) -> None: + for reprlocation, lines in self.reprlocation_lines: + for line in lines: + tw.line(line) + reprlocation.toterminal(tw) + + +class MultipleDoctestFailures(Exception): + def __init__(self, failures: Sequence[doctest.DocTestFailure]) -> None: + super().__init__() + self.failures = failures + + +def _init_runner_class() -> type[doctest.DocTestRunner]: + import doctest + + class PytestDoctestRunner(doctest.DebugRunner): + """Runner to collect failures. + + Note that the out variable in this case is a list instead of a + stdout-like object. + """ + + def __init__( + self, + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, + optionflags: int = 0, + continue_on_failure: bool = True, + ) -> None: + super().__init__(checker=checker, verbose=verbose, optionflags=optionflags) + self.continue_on_failure = continue_on_failure + + def report_failure( + self, + out, + test: doctest.DocTest, + example: doctest.Example, + got: str, + ) -> None: + failure = doctest.DocTestFailure(test, example, got) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + def report_unexpected_exception( + self, + out, + test: doctest.DocTest, + example: doctest.Example, + exc_info: tuple[type[BaseException], BaseException, types.TracebackType], + ) -> None: + if isinstance(exc_info[1], OutcomeException): + raise exc_info[1] + if isinstance(exc_info[1], bdb.BdbQuit): + outcomes.exit("Quitting debugger") + failure = doctest.UnexpectedException(test, example, exc_info) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + return PytestDoctestRunner + + +def _get_runner( + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, + optionflags: int = 0, + continue_on_failure: bool = True, +) -> doctest.DocTestRunner: + # We need this in order to do a lazy import on doctest + global RUNNER_CLASS + if RUNNER_CLASS is None: + RUNNER_CLASS = _init_runner_class() + # Type ignored because the continue_on_failure argument is only defined on + # PytestDoctestRunner, which is lazily defined so can't be used as a type. + return RUNNER_CLASS( # type: ignore + checker=checker, + verbose=verbose, + optionflags=optionflags, + continue_on_failure=continue_on_failure, + ) + + +class DoctestItem(Item): + def __init__( + self, + name: str, + parent: DoctestTextfile | DoctestModule, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> None: + super().__init__(name, parent) + self.runner = runner + self.dtest = dtest + + # Stuff needed for fixture support. + self.obj = None + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(node=self, func=None, cls=None) + self._fixtureinfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + @classmethod + def from_parent( # type: ignore[override] + cls, + parent: DoctestTextfile | DoctestModule, + *, + name: str, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> Self: + # incompatible signature due to imposed limits on subclass + """The public named constructor.""" + return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) + + def _initrequest(self) -> None: + self.funcargs: dict[str, object] = {} + self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] + + def setup(self) -> None: + self._request._fillfixtures() + globs = dict(getfixture=self._request.getfixturevalue) + for name, value in self._request.getfixturevalue("doctest_namespace").items(): + globs[name] = value + self.dtest.globs.update(globs) + + def runtest(self) -> None: + _check_all_skipped(self.dtest) + self._disable_output_capturing_for_darwin() + failures: list[doctest.DocTestFailure] = [] + # Type ignored because we change the type of `out` from what + # doctest expects. + self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] + if failures: + raise MultipleDoctestFailures(failures) + + def _disable_output_capturing_for_darwin(self) -> None: + """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" + if platform.system() != "Darwin": + return + capman = self.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, + excinfo: ExceptionInfo[BaseException], + ) -> str | TerminalRepr: + import doctest + + failures: ( + Sequence[doctest.DocTestFailure | doctest.UnexpectedException] | None + ) = None + if isinstance( + excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) + ): + failures = [excinfo.value] + elif isinstance(excinfo.value, MultipleDoctestFailures): + failures = excinfo.value.failures + + if failures is None: + return super().repr_failure(excinfo) + + reprlocation_lines = [] + for failure in failures: + example = failure.example + test = failure.test + filename = test.filename + if test.lineno is None: + lineno = None + else: + lineno = test.lineno + example.lineno + 1 + message = type(failure).__name__ + # TODO: ReprFileLocation doesn't expect a None lineno. + reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] + checker = _get_checker() + report_choice = _get_report_choice(self.config.getoption("doctestreport")) + if lineno is not None: + assert failure.test.docstring is not None + lines = failure.test.docstring.splitlines(False) + # add line numbers to the left of the error message + assert test.lineno is not None + lines = [ + f"{i + test.lineno + 1:03d} {x}" for (i, x) in enumerate(lines) + ] + # trim docstring error lines to 10 + lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] + else: + lines = [ + "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" + ] + indent = ">>>" + for line in example.source.splitlines(): + lines.append(f"??? {indent} {line}") + indent = "..." + if isinstance(failure, doctest.DocTestFailure): + lines += checker.output_difference( + example, failure.got, report_choice + ).split("\n") + else: + inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) + lines += [f"UNEXPECTED EXCEPTION: {inner_excinfo.value!r}"] + lines += [ + x.strip("\n") for x in traceback.format_exception(*failure.exc_info) + ] + reprlocation_lines.append((reprlocation, lines)) + return ReprFailDoctest(reprlocation_lines) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + return self.path, self.dtest.lineno, f"[doctest] {self.name}" + + +def _get_flag_lookup() -> dict[str, int]: + import doctest + + return dict( + DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1, + DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE, + NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE, + ELLIPSIS=doctest.ELLIPSIS, + IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL, + COMPARISON_FLAGS=doctest.COMPARISON_FLAGS, + ALLOW_UNICODE=_get_allow_unicode_flag(), + ALLOW_BYTES=_get_allow_bytes_flag(), + NUMBER=_get_number_flag(), + ) + + +def get_optionflags(config: Config) -> int: + optionflags_str = config.getini("doctest_optionflags") + flag_lookup_table = _get_flag_lookup() + flag_acc = 0 + for flag in optionflags_str: + flag_acc |= flag_lookup_table[flag] + return flag_acc + + +def _get_continue_on_failure(config: Config) -> bool: + continue_on_failure: bool = config.getvalue("doctest_continue_on_failure") + if continue_on_failure: + # We need to turn off this if we use pdb since we should stop at + # the first failure. + if config.getvalue("usepdb"): + continue_on_failure = False + return continue_on_failure + + +class DoctestTextfile(Module): + obj = None + + def collect(self) -> Iterable[DoctestItem]: + import doctest + + # Inspired by doctest.testfile; ideally we would use it directly, + # but it doesn't support passing a custom checker. + encoding = self.config.getini("doctest_encoding") + text = self.path.read_text(encoding) + filename = str(self.path) + name = self.path.name + globs = {"__name__": "__main__"} + + optionflags = get_optionflags(self.config) + + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + parser = doctest.DocTestParser() + test = parser.get_doctest(text, globs, name, filename, 0) + if test.examples: + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _check_all_skipped(test: doctest.DocTest) -> None: + """Raise pytest.skip() if all examples in the given DocTest have the SKIP + option set.""" + import doctest + + all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) + if all_skipped: + skip("all tests skipped by +SKIP option") + + +def _is_mocked(obj: object) -> bool: + """Return if an object is possibly a mock object by checking the + existence of a highly improbable attribute.""" + return ( + safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) + is not None + ) + + +@contextmanager +def _patch_unwrap_mock_aware() -> Generator[None]: + """Context manager which replaces ``inspect.unwrap`` with a version + that's aware of mock objects and doesn't recurse into them.""" + real_unwrap = inspect.unwrap + + def _mock_aware_unwrap( + func: Callable[..., Any], *, stop: Callable[[Any], Any] | None = None + ) -> Any: + try: + if stop is None or stop is _is_mocked: + return real_unwrap(func, stop=_is_mocked) + _stop = stop + return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) + except Exception as e: + warnings.warn( + f"Got {e!r} when unwrapping {func!r}. This is usually caused " + "by a violation of Python's object protocol; see e.g. " + "https://github.com/pytest-dev/pytest/issues/5080", + PytestWarning, + ) + raise + + inspect.unwrap = _mock_aware_unwrap + try: + yield + finally: + inspect.unwrap = real_unwrap + + +class DoctestModule(Module): + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + py_ver_info_minor = sys.version_info[:2] + is_find_lineno_broken = ( + py_ver_info_minor < (3, 11) + or (py_ver_info_minor == (3, 11) and sys.version_info.micro < 9) + or (py_ver_info_minor == (3, 12) and sys.version_info.micro < 3) + ) + if is_find_lineno_broken: + + def _find_lineno(self, obj, source_lines): + """On older Pythons, doctest code does not take into account + `@property`. https://github.com/python/cpython/issues/61648 + + Moreover, wrapped Doctests need to be unwrapped so the correct + line number is returned. #8796 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + + if hasattr(obj, "__wrapped__"): + # Get the main obj in case of it being wrapped + obj = inspect.unwrap(obj) + + # Type ignored because this is a private function. + return super()._find_lineno( # type:ignore[misc] + obj, + source_lines, + ) + + if sys.version_info < (3, 10): + + def _find( + self, tests, obj, name, module, source_lines, globs, seen + ) -> None: + """Override _find to work around issue in stdlib. + + https://github.com/pytest-dev/pytest/issues/3456 + https://github.com/python/cpython/issues/69718 + """ + if _is_mocked(obj): + return # pragma: no cover + with _patch_unwrap_mock_aware(): + # Type ignored because this is a private function. + super()._find( # type:ignore[misc] + tests, obj, name, module, source_lines, globs, seen + ) + + if sys.version_info < (3, 13): + + def _from_module(self, module, object): + """`cached_property` objects are never considered a part + of the 'current module'. As such they are skipped by doctest. + Here we override `_from_module` to check the underlying + function instead. https://github.com/python/cpython/issues/107995 + """ + if isinstance(object, functools.cached_property): + object = object.func + + # Type ignored because this is a private function. + return super()._from_module(module, object) # type: ignore[misc] + + try: + module = self.obj + except Collector.CollectError: + if self.config.getvalue("doctest_ignore_import_errors"): + skip(f"unable to import module {self.path!r}") + else: + raise + + # While doctests currently don't support fixtures directly, we still + # need to pick up autouse fixtures. + self.session._fixturemanager.parsefactories(self) + + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self.config) + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _init_checker_class() -> type[doctest.OutputChecker]: + import doctest + + class LiteralsOutputChecker(doctest.OutputChecker): + # Based on doctest_nose_plugin.py from the nltk project + # (https://github.com/nltk/nltk) and on the "numtest" doctest extension + # by Sebastien Boisgerault (https://github.com/boisgera/numtest). + + _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) + _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE) + _number_re = re.compile( + r""" + (?P + (?P + (?P [+-]?\d*)\.(?P\d+) + | + (?P [+-]?\d+)\. + ) + (?: + [Ee] + (?P [+-]?\d+) + )? + | + (?P [+-]?\d+) + (?: + [Ee] + (?P [+-]?\d+) + ) + ) + """, + re.VERBOSE, + ) + + def check_output(self, want: str, got: str, optionflags: int) -> bool: + if super().check_output(want, got, optionflags): + return True + + allow_unicode = optionflags & _get_allow_unicode_flag() + allow_bytes = optionflags & _get_allow_bytes_flag() + allow_number = optionflags & _get_number_flag() + + if not allow_unicode and not allow_bytes and not allow_number: + return False + + def remove_prefixes(regex: re.Pattern[str], txt: str) -> str: + return re.sub(regex, r"\1\2", txt) + + if allow_unicode: + want = remove_prefixes(self._unicode_literal_re, want) + got = remove_prefixes(self._unicode_literal_re, got) + + if allow_bytes: + want = remove_prefixes(self._bytes_literal_re, want) + got = remove_prefixes(self._bytes_literal_re, got) + + if allow_number: + got = self._remove_unwanted_precision(want, got) + + return super().check_output(want, got, optionflags) + + def _remove_unwanted_precision(self, want: str, got: str) -> str: + wants = list(self._number_re.finditer(want)) + gots = list(self._number_re.finditer(got)) + if len(wants) != len(gots): + return got + offset = 0 + for w, g in zip(wants, gots): + fraction: str | None = w.group("fraction") + exponent: str | None = w.group("exponent1") + if exponent is None: + exponent = w.group("exponent2") + precision = 0 if fraction is None else len(fraction) + if exponent is not None: + precision -= int(exponent) + if float(w.group()) == approx(float(g.group()), abs=10**-precision): + # They're close enough. Replace the text we actually + # got with the text we want, so that it will match when we + # check the string literally. + got = ( + got[: g.start() + offset] + w.group() + got[g.end() + offset :] + ) + offset += w.end() - w.start() - (g.end() - g.start()) + return got + + return LiteralsOutputChecker + + +def _get_checker() -> doctest.OutputChecker: + """Return a doctest.OutputChecker subclass that supports some + additional options: + + * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b'' + prefixes (respectively) in string literals. Useful when the same + doctest should run in Python 2 and Python 3. + + * NUMBER to ignore floating-point differences smaller than the + precision of the literal number in the doctest. + + An inner class is used to avoid importing "doctest" at the module + level. + """ + global CHECKER_CLASS + if CHECKER_CLASS is None: + CHECKER_CLASS = _init_checker_class() + return CHECKER_CLASS() + + +def _get_allow_unicode_flag() -> int: + """Register and return the ALLOW_UNICODE flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_UNICODE") + + +def _get_allow_bytes_flag() -> int: + """Register and return the ALLOW_BYTES flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_BYTES") + + +def _get_number_flag() -> int: + """Register and return the NUMBER flag.""" + import doctest + + return doctest.register_optionflag("NUMBER") + + +def _get_report_choice(key: str) -> int: + """Return the actual `doctest` module flag value. + + We want to do it as late as possible to avoid importing `doctest` and all + its dependencies when parsing options, as it adds overhead and breaks tests. + """ + import doctest + + return { + DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, + DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, + DOCTEST_REPORT_CHOICE_NONE: 0, + }[key] + + +@fixture(scope="session") +def doctest_namespace() -> dict[str, Any]: + """Fixture that returns a :py:class:`dict` that will be injected into the + namespace of doctests. + + Usually this fixture is used in conjunction with another ``autouse`` fixture: + + .. code-block:: python + + @pytest.fixture(autouse=True) + def add_np(doctest_namespace): + doctest_namespace["np"] = numpy + + For more details: :ref:`doctest_namespace`. + """ + return dict() diff --git a/.venv/lib/python3.11/site-packages/_pytest/faulthandler.py b/.venv/lib/python3.11/site-packages/_pytest/faulthandler.py new file mode 100644 index 00000000..79efc1d1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/faulthandler.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from collections.abc import Generator +import os +import sys + +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.stash import StashKey +import pytest + + +fault_handler_original_stderr_fd_key = StashKey[int]() +fault_handler_stderr_fd_key = StashKey[int]() + + +def pytest_addoption(parser: Parser) -> None: + help = ( + "Dump the traceback of all threads if a test takes " + "more than TIMEOUT seconds to finish" + ) + parser.addini("faulthandler_timeout", help, default=0.0) + + +def pytest_configure(config: Config) -> None: + import faulthandler + + # at teardown we want to restore the original faulthandler fileno + # but faulthandler has no api to return the original fileno + # so here we stash the stderr fileno to be used at teardown + # sys.stderr and sys.__stderr__ may be closed or patched during the session + # so we can't rely on their values being good at that point (#11572). + stderr_fileno = get_stderr_fileno() + if faulthandler.is_enabled(): + config.stash[fault_handler_original_stderr_fd_key] = stderr_fileno + config.stash[fault_handler_stderr_fd_key] = os.dup(stderr_fileno) + faulthandler.enable(file=config.stash[fault_handler_stderr_fd_key]) + + +def pytest_unconfigure(config: Config) -> None: + import faulthandler + + faulthandler.disable() + # Close the dup file installed during pytest_configure. + if fault_handler_stderr_fd_key in config.stash: + os.close(config.stash[fault_handler_stderr_fd_key]) + del config.stash[fault_handler_stderr_fd_key] + # Re-enable the faulthandler if it was originally enabled. + if fault_handler_original_stderr_fd_key in config.stash: + faulthandler.enable(config.stash[fault_handler_original_stderr_fd_key]) + del config.stash[fault_handler_original_stderr_fd_key] + + +def get_stderr_fileno() -> int: + try: + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno + except (AttributeError, ValueError): + # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. + # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors + # This is potentially dangerous, but the best we can do. + assert sys.__stderr__ is not None + return sys.__stderr__.fileno() + + +def get_timeout_config_value(config: Config) -> float: + return float(config.getini("faulthandler_timeout") or 0.0) + + +@pytest.hookimpl(wrapper=True, trylast=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + timeout = get_timeout_config_value(item.config) + if timeout > 0: + import faulthandler + + stderr = item.config.stash[fault_handler_stderr_fd_key] + faulthandler.dump_traceback_later(timeout, file=stderr) + try: + return (yield) + finally: + faulthandler.cancel_dump_traceback_later() + else: + return (yield) + + +@pytest.hookimpl(tryfirst=True) +def pytest_enter_pdb() -> None: + """Cancel any traceback dumping due to timeout before entering pdb.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() + + +@pytest.hookimpl(tryfirst=True) +def pytest_exception_interact() -> None: + """Cancel any traceback dumping due to an interactive exception being + raised.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() diff --git a/.venv/lib/python3.11/site-packages/_pytest/fixtures.py b/.venv/lib/python3.11/site-packages/_pytest/fixtures.py new file mode 100644 index 00000000..421237e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/fixtures.py @@ -0,0 +1,2017 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import abc +from collections import defaultdict +from collections import deque +from collections import OrderedDict +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import dataclasses +import functools +import inspect +import os +from pathlib import Path +import sys +import types +from typing import Any +from typing import cast +from typing import Final +from typing import final +from typing import Generic +from typing import NoReturn +from typing import Optional +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +import warnings + +import _pytest +from _pytest import nodes +from _pytest._code import getfslineno +from _pytest._code import Source +from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import assert_never +from _pytest.compat import get_real_func +from _pytest.compat import getfuncargnames +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import NOTSET +from _pytest.compat import NotSetType +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.compat import signature +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import MARKED_FIXTURE +from _pytest.deprecated import YIELD_FIXTURE +from _pytest.main import Session +from _pytest.mark import Mark +from _pytest.mark import ParameterSet +from _pytest.mark.structures import MarkDecorator +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import TEST_OUTCOME +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.scope import _ScopeName +from _pytest.scope import HIGH_SCOPES +from _pytest.scope import Scope +from _pytest.warning_types import PytestRemovedIn9Warning +from _pytest.warning_types import PytestWarning + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +if TYPE_CHECKING: + from _pytest.python import CallSpec2 + from _pytest.python import Function + from _pytest.python import Metafunc + + +# The value of the fixture -- return/yield of the fixture function (type variable). +FixtureValue = TypeVar("FixtureValue") +# The type of the fixture function (type variable). +FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) +# The type of a fixture function (type alias generic in fixture value). +_FixtureFunc = Union[ + Callable[..., FixtureValue], Callable[..., Generator[FixtureValue]] +] +# The type of FixtureDef.cached_result (type alias generic in fixture value). +_FixtureCachedResult = Union[ + tuple[ + # The result. + FixtureValue, + # Cache key. + object, + None, + ], + tuple[ + None, + # Cache key. + object, + # The exception and the original traceback. + tuple[BaseException, Optional[types.TracebackType]], + ], +] + + +@dataclasses.dataclass(frozen=True) +class PseudoFixtureDef(Generic[FixtureValue]): + cached_result: _FixtureCachedResult[FixtureValue] + _scope: Scope + + +def pytest_sessionstart(session: Session) -> None: + session._fixturemanager = FixtureManager(session) + + +def get_scope_package( + node: nodes.Item, + fixturedef: FixtureDef[object], +) -> nodes.Node | None: + from _pytest.python import Package + + for parent in node.iter_parents(): + if isinstance(parent, Package) and parent.nodeid == fixturedef.baseid: + return parent + return node.session + + +def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None: + """Get the closest parent node (including self) which matches the given + scope. + + If there is no parent node for the scope (e.g. asking for class scope on a + Module, or on a Function when not defined in a class), returns None. + """ + import _pytest.python + + if scope is Scope.Function: + # Type ignored because this is actually safe, see: + # https://github.com/python/mypy/issues/4717 + return node.getparent(nodes.Item) # type: ignore[type-abstract] + elif scope is Scope.Class: + return node.getparent(_pytest.python.Class) + elif scope is Scope.Module: + return node.getparent(_pytest.python.Module) + elif scope is Scope.Package: + return node.getparent(_pytest.python.Package) + elif scope is Scope.Session: + return node.getparent(_pytest.main.Session) + else: + assert_never(scope) + + +# TODO: Try to use FixtureFunctionDefinition instead of the marker +def getfixturemarker(obj: object) -> FixtureFunctionMarker | None: + """Return fixturemarker or None if it doesn't exist""" + if isinstance(obj, FixtureFunctionDefinition): + return obj._fixture_function_marker + return None + + +# Algorithm for sorting on a per-parametrized resource setup basis. +# It is called for Session scope first and performs sorting +# down to the lower scopes such as to minimize number of "high scope" +# setups and teardowns. + + +@dataclasses.dataclass(frozen=True) +class ParamArgKey: + """A key for a high-scoped parameter used by an item. + + For use as a hashable key in `reorder_items`. The combination of fields + is meant to uniquely identify a particular "instance" of a param, + potentially shared by multiple items in a scope. + """ + + #: The param name. + argname: str + param_index: int + #: For scopes Package, Module, Class, the path to the file (directory in + #: Package's case) of the package/module/class where the item is defined. + scoped_item_path: Path | None + #: For Class scope, the class where the item is defined. + item_cls: type | None + + +_V = TypeVar("_V") +OrderedSet = dict[_V, None] + + +def get_param_argkeys(item: nodes.Item, scope: Scope) -> Iterator[ParamArgKey]: + """Return all ParamArgKeys for item matching the specified high scope.""" + assert scope is not Scope.Function + + try: + callspec: CallSpec2 = item.callspec # type: ignore[attr-defined] + except AttributeError: + return + + item_cls = None + if scope is Scope.Session: + scoped_item_path = None + elif scope is Scope.Package: + # Package key = module's directory. + scoped_item_path = item.path.parent + elif scope is Scope.Module: + scoped_item_path = item.path + elif scope is Scope.Class: + scoped_item_path = item.path + item_cls = item.cls # type: ignore[attr-defined] + else: + assert_never(scope) + + for argname in callspec.indices: + if callspec._arg2scope[argname] != scope: + continue + param_index = callspec.indices[argname] + yield ParamArgKey(argname, param_index, scoped_item_path, item_cls) + + +def reorder_items(items: Sequence[nodes.Item]) -> list[nodes.Item]: + argkeys_by_item: dict[Scope, dict[nodes.Item, OrderedSet[ParamArgKey]]] = {} + items_by_argkey: dict[Scope, dict[ParamArgKey, OrderedDict[nodes.Item, None]]] = {} + for scope in HIGH_SCOPES: + scoped_argkeys_by_item = argkeys_by_item[scope] = {} + scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(OrderedDict) + for item in items: + argkeys = dict.fromkeys(get_param_argkeys(item, scope)) + if argkeys: + scoped_argkeys_by_item[item] = argkeys + for argkey in argkeys: + scoped_items_by_argkey[argkey][item] = None + + items_set = dict.fromkeys(items) + return list( + reorder_items_atscope( + items_set, argkeys_by_item, items_by_argkey, Scope.Session + ) + ) + + +def reorder_items_atscope( + items: OrderedSet[nodes.Item], + argkeys_by_item: Mapping[Scope, Mapping[nodes.Item, OrderedSet[ParamArgKey]]], + items_by_argkey: Mapping[ + Scope, Mapping[ParamArgKey, OrderedDict[nodes.Item, None]] + ], + scope: Scope, +) -> OrderedSet[nodes.Item]: + if scope is Scope.Function or len(items) < 3: + return items + + scoped_items_by_argkey = items_by_argkey[scope] + scoped_argkeys_by_item = argkeys_by_item[scope] + + ignore: set[ParamArgKey] = set() + items_deque = deque(items) + items_done: OrderedSet[nodes.Item] = {} + while items_deque: + no_argkey_items: OrderedSet[nodes.Item] = {} + slicing_argkey = None + while items_deque: + item = items_deque.popleft() + if item in items_done or item in no_argkey_items: + continue + argkeys = dict.fromkeys( + k for k in scoped_argkeys_by_item.get(item, ()) if k not in ignore + ) + if not argkeys: + no_argkey_items[item] = None + else: + slicing_argkey, _ = argkeys.popitem() + # We don't have to remove relevant items from later in the + # deque because they'll just be ignored. + matching_items = [ + i for i in scoped_items_by_argkey[slicing_argkey] if i in items + ] + for i in reversed(matching_items): + items_deque.appendleft(i) + # Fix items_by_argkey order. + for other_scope in HIGH_SCOPES: + other_scoped_items_by_argkey = items_by_argkey[other_scope] + for argkey in argkeys_by_item[other_scope].get(i, ()): + argkey_dict = other_scoped_items_by_argkey[argkey] + if not hasattr(sys, "pypy_version_info"): + argkey_dict[i] = None + argkey_dict.move_to_end(i, last=False) + else: + # Work around a bug in PyPy: + # https://github.com/pypy/pypy/issues/5257 + # https://github.com/pytest-dev/pytest/issues/13312 + bkp = argkey_dict.copy() + argkey_dict.clear() + argkey_dict[i] = None + argkey_dict.update(bkp) + break + if no_argkey_items: + reordered_no_argkey_items = reorder_items_atscope( + no_argkey_items, argkeys_by_item, items_by_argkey, scope.next_lower() + ) + items_done.update(reordered_no_argkey_items) + if slicing_argkey is not None: + ignore.add(slicing_argkey) + return items_done + + +@dataclasses.dataclass(frozen=True) +class FuncFixtureInfo: + """Fixture-related information for a fixture-requesting item (e.g. test + function). + + This is used to examine the fixtures which an item requests statically + (known during collection). This includes autouse fixtures, fixtures + requested by the `usefixtures` marker, fixtures requested in the function + parameters, and the transitive closure of these. + + An item may also request fixtures dynamically (using `request.getfixturevalue`); + these are not reflected here. + """ + + __slots__ = ("argnames", "initialnames", "name2fixturedefs", "names_closure") + + # Fixture names that the item requests directly by function parameters. + argnames: tuple[str, ...] + # Fixture names that the item immediately requires. These include + # argnames + fixture names specified via usefixtures and via autouse=True in + # fixture definitions. + initialnames: tuple[str, ...] + # The transitive closure of the fixture names that the item requires. + # Note: can't include dynamic dependencies (`request.getfixturevalue` calls). + names_closure: list[str] + # A map from a fixture name in the transitive closure to the FixtureDefs + # matching the name which are applicable to this function. + # There may be multiple overriding fixtures with the same name. The + # sequence is ordered from furthest to closes to the function. + name2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] + + def prune_dependency_tree(self) -> None: + """Recompute names_closure from initialnames and name2fixturedefs. + + Can only reduce names_closure, which means that the new closure will + always be a subset of the old one. The order is preserved. + + This method is needed because direct parametrization may shadow some + of the fixtures that were included in the originally built dependency + tree. In this way the dependency tree can get pruned, and the closure + of argnames may get reduced. + """ + closure: set[str] = set() + working_set = set(self.initialnames) + while working_set: + argname = working_set.pop() + # Argname may be something not included in the original names_closure, + # in which case we ignore it. This currently happens with pseudo + # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. + # So they introduce the new dependency 'request' which might have + # been missing in the original tree (closure). + if argname not in closure and argname in self.names_closure: + closure.add(argname) + if argname in self.name2fixturedefs: + working_set.update(self.name2fixturedefs[argname][-1].argnames) + + self.names_closure[:] = sorted(closure, key=self.names_closure.index) + + +class FixtureRequest(abc.ABC): + """The type of the ``request`` fixture. + + A request object gives access to the requesting test context and has a + ``param`` attribute in case the fixture is parametrized. + """ + + def __init__( + self, + pyfuncitem: Function, + fixturename: str | None, + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]], + fixture_defs: dict[str, FixtureDef[Any]], + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + #: Fixture for which this request is being performed. + self.fixturename: Final = fixturename + self._pyfuncitem: Final = pyfuncitem + # The FixtureDefs for each fixture name requested by this item. + # Starts from the statically-known fixturedefs resolved during + # collection. Dynamically requested fixtures (using + # `request.getfixturevalue("foo")`) are added dynamically. + self._arg2fixturedefs: Final = arg2fixturedefs + # The evaluated argnames so far, mapping to the FixtureDef they resolved + # to. + self._fixture_defs: Final = fixture_defs + # Notes on the type of `param`: + # -`request.param` is only defined in parametrized fixtures, and will raise + # AttributeError otherwise. Python typing has no notion of "undefined", so + # this cannot be reflected in the type. + # - Technically `param` is only (possibly) defined on SubRequest, not + # FixtureRequest, but the typing of that is still in flux so this cheats. + # - In the future we might consider using a generic for the param type, but + # for now just using Any. + self.param: Any + + @property + def _fixturemanager(self) -> FixtureManager: + return self._pyfuncitem.session._fixturemanager + + @property + @abc.abstractmethod + def _scope(self) -> Scope: + raise NotImplementedError() + + @property + def scope(self) -> _ScopeName: + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + + @abc.abstractmethod + def _check_scope( + self, + requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], + requested_scope: Scope, + ) -> None: + raise NotImplementedError() + + @property + def fixturenames(self) -> list[str]: + """Names of all active fixtures in this request.""" + result = list(self._pyfuncitem.fixturenames) + result.extend(set(self._fixture_defs).difference(result)) + return result + + @property + @abc.abstractmethod + def node(self): + """Underlying collection node (depends on current request scope).""" + raise NotImplementedError() + + @property + def config(self) -> Config: + """The pytest config object associated with this request.""" + return self._pyfuncitem.config + + @property + def function(self): + """Test function object if the request has a per-function scope.""" + if self.scope != "function": + raise AttributeError( + f"function not available in {self.scope}-scoped context" + ) + return self._pyfuncitem.obj + + @property + def cls(self): + """Class (can be None) where the test function was collected.""" + if self.scope not in ("class", "function"): + raise AttributeError(f"cls not available in {self.scope}-scoped context") + clscol = self._pyfuncitem.getparent(_pytest.python.Class) + if clscol: + return clscol.obj + + @property + def instance(self): + """Instance (can be None) on which test function was collected.""" + if self.scope != "function": + return None + return getattr(self._pyfuncitem, "instance", None) + + @property + def module(self): + """Python module object where the test function was collected.""" + if self.scope not in ("function", "class", "module"): + raise AttributeError(f"module not available in {self.scope}-scoped context") + mod = self._pyfuncitem.getparent(_pytest.python.Module) + assert mod is not None + return mod.obj + + @property + def path(self) -> Path: + """Path where the test function was collected.""" + if self.scope not in ("function", "class", "module", "package"): + raise AttributeError(f"path not available in {self.scope}-scoped context") + return self._pyfuncitem.path + + @property + def keywords(self) -> MutableMapping[str, Any]: + """Keywords/markers dictionary for the underlying node.""" + node: nodes.Node = self.node + return node.keywords + + @property + def session(self) -> Session: + """Pytest session object.""" + return self._pyfuncitem.session + + @abc.abstractmethod + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called without arguments after + the last test within the requesting test context finished execution.""" + raise NotImplementedError() + + def applymarker(self, marker: str | MarkDecorator) -> None: + """Apply a marker to a single test function invocation. + + This method is useful if you don't want to have a keyword/marker + on all function invocations. + + :param marker: + An object created by a call to ``pytest.mark.NAME(...)``. + """ + self.node.add_marker(marker) + + def raiseerror(self, msg: str | None) -> NoReturn: + """Raise a FixtureLookupError exception. + + :param msg: + An optional custom error message. + """ + raise FixtureLookupError(None, self, msg) + + def getfixturevalue(self, argname: str) -> Any: + """Dynamically run a named fixture function. + + Declaring fixtures via function argument is recommended where possible. + But if you can only decide whether to use another fixture at test + setup time, you may use this function to retrieve it inside a fixture + or test function body. + + This method can be used during the test setup phase or the test run + phase, but during the test teardown phase a fixture's value may not + be available. + + :param argname: + The fixture name. + :raises pytest.FixtureLookupError: + If the given fixture could not be found. + """ + # Note that in addition to the use case described in the docstring, + # getfixturevalue() is also called by pytest itself during item and fixture + # setup to evaluate the fixtures that are requested statically + # (using function parameters, autouse, etc). + + fixturedef = self._get_active_fixturedef(argname) + assert fixturedef.cached_result is not None, ( + f'The fixture value for "{argname}" is not available. ' + "This can happen when the fixture has already been torn down." + ) + return fixturedef.cached_result[0] + + def _iter_chain(self) -> Iterator[SubRequest]: + """Yield all SubRequests in the chain, from self up. + + Note: does *not* yield the TopRequest. + """ + current = self + while isinstance(current, SubRequest): + yield current + current = current._parent_request + + def _get_active_fixturedef( + self, argname: str + ) -> FixtureDef[object] | PseudoFixtureDef[object]: + if argname == "request": + cached_result = (self, [0], None) + return PseudoFixtureDef(cached_result, Scope.Function) + + # If we already finished computing a fixture by this name in this item, + # return it. + fixturedef = self._fixture_defs.get(argname) + if fixturedef is not None: + self._check_scope(fixturedef, fixturedef._scope) + return fixturedef + + # Find the appropriate fixturedef. + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # We arrive here because of a dynamic call to + # getfixturevalue(argname) which was naturally + # not known at parsing/collection time. + fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem) + if fixturedefs is not None: + self._arg2fixturedefs[argname] = fixturedefs + # No fixtures defined with this name. + if fixturedefs is None: + raise FixtureLookupError(argname, self) + # The are no fixtures with this name applicable for the function. + if not fixturedefs: + raise FixtureLookupError(argname, self) + + # A fixture may override another fixture with the same name, e.g. a + # fixture in a module can override a fixture in a conftest, a fixture in + # a class can override a fixture in the module, and so on. + # An overriding fixture can request its own name (possibly indirectly); + # in this case it gets the value of the fixture it overrides, one level + # up. + # Check how many `argname`s deep we are, and take the next one. + # `fixturedefs` is sorted from furthest to closest, so use negative + # indexing to go in reverse. + index = -1 + for request in self._iter_chain(): + if request.fixturename == argname: + index -= 1 + # If already consumed all of the available levels, fail. + if -index > len(fixturedefs): + raise FixtureLookupError(argname, self) + fixturedef = fixturedefs[index] + + # Prepare a SubRequest object for calling the fixture. + try: + callspec = self._pyfuncitem.callspec + except AttributeError: + callspec = None + if callspec is not None and argname in callspec.params: + param = callspec.params[argname] + param_index = callspec.indices[argname] + # The parametrize invocation scope overrides the fixture's scope. + scope = callspec._arg2scope[argname] + else: + param = NOTSET + param_index = 0 + scope = fixturedef._scope + self._check_fixturedef_without_param(fixturedef) + # The parametrize invocation scope only controls caching behavior while + # allowing wider-scoped fixtures to keep depending on the parametrized + # fixture. Scope control is enforced for parametrized fixtures + # by recreating the whole fixture tree on parameter change. + # Hence `fixturedef._scope`, not `scope`. + self._check_scope(fixturedef, fixturedef._scope) + subrequest = SubRequest( + self, scope, param, param_index, fixturedef, _ispytest=True + ) + + # Make sure the fixture value is cached, running it if it isn't + fixturedef.execute(request=subrequest) + + self._fixture_defs[argname] = fixturedef + return fixturedef + + def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> None: + """Check that this request is allowed to execute this fixturedef without + a param.""" + funcitem = self._pyfuncitem + has_params = fixturedef.params is not None + fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + if has_params and fixtures_not_supported: + msg = ( + f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" + f"Node id: {funcitem.nodeid}\n" + f"Function type: {type(funcitem).__name__}" + ) + fail(msg, pytrace=False) + if has_params: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = absolutepath(frameinfo.filename) + source_lineno = frameinfo.lineno + try: + source_path_str = str(source_path.relative_to(funcitem.config.rootpath)) + except ValueError: + source_path_str = str(source_path) + location = getlocation(fixturedef.func, funcitem.config.rootpath) + msg = ( + "The requested fixture has no parameter defined for test:\n" + f" {funcitem.nodeid}\n\n" + f"Requested fixture '{fixturedef.argname}' defined in:\n" + f"{location}\n\n" + f"Requested here:\n" + f"{source_path_str}:{source_lineno}" + ) + fail(msg, pytrace=False) + + def _get_fixturestack(self) -> list[FixtureDef[Any]]: + values = [request._fixturedef for request in self._iter_chain()] + values.reverse() + return values + + +@final +class TopRequest(FixtureRequest): + """The type of the ``request`` fixture in a test function.""" + + def __init__(self, pyfuncitem: Function, *, _ispytest: bool = False) -> None: + super().__init__( + fixturename=None, + pyfuncitem=pyfuncitem, + arg2fixturedefs=pyfuncitem._fixtureinfo.name2fixturedefs.copy(), + fixture_defs={}, + _ispytest=_ispytest, + ) + + @property + def _scope(self) -> Scope: + return Scope.Function + + def _check_scope( + self, + requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], + requested_scope: Scope, + ) -> None: + # TopRequest always has function scope so always valid. + pass + + @property + def node(self): + return self._pyfuncitem + + def __repr__(self) -> str: + return f"" + + def _fillfixtures(self) -> None: + item = self._pyfuncitem + for argname in item.fixturenames: + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self.node.addfinalizer(finalizer) + + +@final +class SubRequest(FixtureRequest): + """The type of the ``request`` fixture in a fixture function requested + (transitively) by a test function.""" + + def __init__( + self, + request: FixtureRequest, + scope: Scope, + param: Any, + param_index: int, + fixturedef: FixtureDef[object], + *, + _ispytest: bool = False, + ) -> None: + super().__init__( + pyfuncitem=request._pyfuncitem, + fixturename=fixturedef.argname, + fixture_defs=request._fixture_defs, + arg2fixturedefs=request._arg2fixturedefs, + _ispytest=_ispytest, + ) + self._parent_request: Final[FixtureRequest] = request + self._scope_field: Final = scope + self._fixturedef: Final[FixtureDef[object]] = fixturedef + if param is not NOTSET: + self.param = param + self.param_index: Final = param_index + + def __repr__(self) -> str: + return f"" + + @property + def _scope(self) -> Scope: + return self._scope_field + + @property + def node(self): + scope = self._scope + if scope is Scope.Function: + # This might also be a non-function Item despite its attribute name. + node: nodes.Node | None = self._pyfuncitem + elif scope is Scope.Package: + node = get_scope_package(self._pyfuncitem, self._fixturedef) + else: + node = get_scope_node(self._pyfuncitem, scope) + if node is None and scope is Scope.Class: + # Fallback to function item itself. + node = self._pyfuncitem + assert node, ( + f'Could not obtain a node for scope "{scope}" for function {self._pyfuncitem!r}' + ) + return node + + def _check_scope( + self, + requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], + requested_scope: Scope, + ) -> None: + if isinstance(requested_fixturedef, PseudoFixtureDef): + return + if self._scope > requested_scope: + # Try to report something helpful. + argname = requested_fixturedef.argname + fixture_stack = "\n".join( + self._format_fixturedef_line(fixturedef) + for fixturedef in self._get_fixturestack() + ) + requested_fixture = self._format_fixturedef_line(requested_fixturedef) + fail( + f"ScopeMismatch: You tried to access the {requested_scope.value} scoped " + f"fixture {argname} with a {self._scope.value} scoped request object. " + f"Requesting fixture stack:\n{fixture_stack}\n" + f"Requested fixture:\n{requested_fixture}", + pytrace=False, + ) + + def _format_fixturedef_line(self, fixturedef: FixtureDef[object]) -> str: + factory = fixturedef.func + path, lineno = getfslineno(factory) + if isinstance(path, Path): + path = bestrelpath(self._pyfuncitem.session.path, path) + sig = signature(factory) + return f"{path}:{lineno + 1}: def {factory.__name__}{sig}" + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._fixturedef.addfinalizer(finalizer) + + +@final +class FixtureLookupError(LookupError): + """Could not return a requested fixture (missing or invalid).""" + + def __init__( + self, argname: str | None, request: FixtureRequest, msg: str | None = None + ) -> None: + self.argname = argname + self.request = request + self.fixturestack = request._get_fixturestack() + self.msg = msg + + def formatrepr(self) -> FixtureLookupErrorRepr: + tblines: list[str] = [] + addline = tblines.append + stack = [self.request._pyfuncitem.obj] + stack.extend(map(lambda x: x.func, self.fixturestack)) + msg = self.msg + # This function currently makes an assumption that a non-None msg means we + # have a non-empty `self.fixturestack`. This is currently true, but if + # somebody at some point want to extend the use of FixtureLookupError to + # new cases it might break. + # Add the assert to make it clearer to developer that this will fail, otherwise + # it crashes because `fspath` does not get set due to `stack` being empty. + assert self.msg is None or self.fixturestack, ( + "formatrepr assumptions broken, rewrite it to handle it" + ) + if msg is not None: + # The last fixture raise an error, let's present + # it at the requesting side. + stack = stack[:-1] + for function in stack: + fspath, lineno = getfslineno(function) + try: + lines, _ = inspect.getsourcelines(get_real_func(function)) + except (OSError, IndexError, TypeError): + error_msg = "file %s, line %s: source code not available" + addline(error_msg % (fspath, lineno + 1)) + else: + addline(f"file {fspath}, line {lineno + 1}") + for i, line in enumerate(lines): + line = line.rstrip() + addline(" " + line) + if line.lstrip().startswith("def"): + break + + if msg is None: + fm = self.request._fixturemanager + available = set() + parent = self.request._pyfuncitem.parent + assert parent is not None + for name, fixturedefs in fm._arg2fixturedefs.items(): + faclist = list(fm._matchfactories(fixturedefs, parent)) + if faclist: + available.add(name) + if self.argname in available: + msg = ( + f" recursive dependency involving fixture '{self.argname}' detected" + ) + else: + msg = f"fixture '{self.argname}' not found" + msg += "\n available fixtures: {}".format(", ".join(sorted(available))) + msg += "\n use 'pytest --fixtures [testpath]' for help on them." + + return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) + + +class FixtureLookupErrorRepr(TerminalRepr): + def __init__( + self, + filename: str | os.PathLike[str], + firstlineno: int, + tblines: Sequence[str], + errorstring: str, + argname: str | None, + ) -> None: + self.tblines = tblines + self.errorstring = errorstring + self.filename = filename + self.firstlineno = firstlineno + self.argname = argname + + def toterminal(self, tw: TerminalWriter) -> None: + # tw.line("FixtureLookupError: %s" %(self.argname), red=True) + for tbline in self.tblines: + tw.line(tbline.rstrip()) + lines = self.errorstring.split("\n") + if lines: + tw.line( + f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", + red=True, + ) + for line in lines[1:]: + tw.line( + f"{FormattedExcinfo.flow_marker} {line.strip()}", + red=True, + ) + tw.line() + tw.line(f"{os.fspath(self.filename)}:{self.firstlineno + 1}") + + +def call_fixture_func( + fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs +) -> FixtureValue: + if inspect.isgeneratorfunction(fixturefunc): + fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc) + generator = fixturefunc(**kwargs) + try: + fixture_result = next(generator) + except StopIteration: + raise ValueError(f"{request.fixturename} did not yield a value") from None + finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) + request.addfinalizer(finalizer) + else: + fixturefunc = cast(Callable[..., FixtureValue], fixturefunc) + fixture_result = fixturefunc(**kwargs) + return fixture_result + + +def _teardown_yield_fixture(fixturefunc, it) -> None: + """Execute the teardown of a fixture function by advancing the iterator + after the yield and ensure the iteration ends (if not it means there is + more than one yield in the function).""" + try: + next(it) + except StopIteration: + pass + else: + fs, lineno = getfslineno(fixturefunc) + fail( + f"fixture function has more than one 'yield':\n\n" + f"{Source(fixturefunc).indent()}\n" + f"{fs}:{lineno + 1}", + pytrace=False, + ) + + +def _eval_scope_callable( + scope_callable: Callable[[str, Config], _ScopeName], + fixture_name: str, + config: Config, +) -> _ScopeName: + try: + # Type ignored because there is no typing mechanism to specify + # keyword arguments, currently. + result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] + except Exception as e: + raise TypeError( + f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n" + "Expected a function with the signature (*, fixture_name, config)" + ) from e + if not isinstance(result, str): + fail( + f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n" + f"{result!r}", + pytrace=False, + ) + return result + + +@final +class FixtureDef(Generic[FixtureValue]): + """A container for a fixture definition. + + Note: At this time, only explicitly documented fields and methods are + considered public stable API. + """ + + def __init__( + self, + config: Config, + baseid: str | None, + argname: str, + func: _FixtureFunc[FixtureValue], + scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None, + params: Sequence[object] | None, + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, + *, + _ispytest: bool = False, + # only used in a deprecationwarning msg, can be removed in pytest9 + _autouse: bool = False, + ) -> None: + check_ispytest(_ispytest) + # The "base" node ID for the fixture. + # + # This is a node ID prefix. A fixture is only available to a node (e.g. + # a `Function` item) if the fixture's baseid is a nodeid of a parent of + # node. + # + # For a fixture found in a Collector's object (e.g. a `Module`s module, + # a `Class`'s class), the baseid is the Collector's nodeid. + # + # For a fixture found in a conftest plugin, the baseid is the conftest's + # directory path relative to the rootdir. + # + # For other plugins, the baseid is the empty string (always matches). + self.baseid: Final = baseid or "" + # Whether the fixture was found from a node or a conftest in the + # collection tree. Will be false for fixtures defined in non-conftest + # plugins. + self.has_location: Final = baseid is not None + # The fixture factory function. + self.func: Final = func + # The name by which the fixture may be requested. + self.argname: Final = argname + if scope is None: + scope = Scope.Function + elif callable(scope): + scope = _eval_scope_callable(scope, argname, config) + if isinstance(scope, str): + scope = Scope.from_user( + scope, descr=f"Fixture '{func.__name__}'", where=baseid + ) + self._scope: Final = scope + # If the fixture is directly parametrized, the parameter values. + self.params: Final = params + # If the fixture is directly parametrized, a tuple of explicit IDs to + # assign to the parameter values, or a callable to generate an ID given + # a parameter value. + self.ids: Final = ids + # The names requested by the fixtures. + self.argnames: Final = getfuncargnames(func, name=argname) + # If the fixture was executed, the current value of the fixture. + # Can change if the fixture is executed with different parameters. + self.cached_result: _FixtureCachedResult[FixtureValue] | None = None + self._finalizers: Final[list[Callable[[], object]]] = [] + + # only used to emit a deprecationwarning, can be removed in pytest9 + self._autouse = _autouse + + @property + def scope(self) -> _ScopeName: + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._finalizers.append(finalizer) + + def finish(self, request: SubRequest) -> None: + exceptions: list[BaseException] = [] + while self._finalizers: + fin = self._finalizers.pop() + try: + fin() + except BaseException as e: + exceptions.append(e) + node = request.node + node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) + # Even if finalization fails, we invalidate the cached fixture + # value and remove all finalizers because they may be bound methods + # which will keep instances alive. + self.cached_result = None + self._finalizers.clear() + if len(exceptions) == 1: + raise exceptions[0] + elif len(exceptions) > 1: + msg = f'errors while tearing down fixture "{self.argname}" of {node}' + raise BaseExceptionGroup(msg, exceptions[::-1]) + + def execute(self, request: SubRequest) -> FixtureValue: + """Return the value of this fixture, executing it if not cached.""" + # Ensure that the dependent fixtures requested by this fixture are loaded. + # This needs to be done before checking if we have a cached value, since + # if a dependent fixture has their cache invalidated, e.g. due to + # parametrization, they finalize themselves and fixtures depending on it + # (which will likely include this fixture) setting `self.cached_result = None`. + # See #4871 + requested_fixtures_that_should_finalize_us = [] + for argname in self.argnames: + fixturedef = request._get_active_fixturedef(argname) + # Saves requested fixtures in a list so we later can add our finalizer + # to them, ensuring that if a requested fixture gets torn down we get torn + # down first. This is generally handled by SetupState, but still currently + # needed when this fixture is not parametrized but depends on a parametrized + # fixture. + if not isinstance(fixturedef, PseudoFixtureDef): + requested_fixtures_that_should_finalize_us.append(fixturedef) + + # Check for (and return) cached value/exception. + if self.cached_result is not None: + request_cache_key = self.cache_key(request) + cache_key = self.cached_result[1] + try: + # Attempt to make a normal == check: this might fail for objects + # which do not implement the standard comparison (like numpy arrays -- #6497). + cache_hit = bool(request_cache_key == cache_key) + except (ValueError, RuntimeError): + # If the comparison raises, use 'is' as fallback. + cache_hit = request_cache_key is cache_key + + if cache_hit: + if self.cached_result[2] is not None: + exc, exc_tb = self.cached_result[2] + raise exc.with_traceback(exc_tb) + else: + result = self.cached_result[0] + return result + # We have a previous but differently parametrized fixture instance + # so we need to tear it down before creating a new one. + self.finish(request) + assert self.cached_result is None + + # Add finalizer to requested fixtures we saved previously. + # We make sure to do this after checking for cached value to avoid + # adding our finalizer multiple times. (#12135) + finalizer = functools.partial(self.finish, request=request) + for parent_fixture in requested_fixtures_that_should_finalize_us: + parent_fixture.addfinalizer(finalizer) + + ihook = request.node.ihook + try: + # Setup the fixture, run the code in it, and cache the value + # in self.cached_result + result = ihook.pytest_fixture_setup(fixturedef=self, request=request) + finally: + # schedule our finalizer, even if the setup failed + request.node.addfinalizer(finalizer) + + return result + + def cache_key(self, request: SubRequest) -> object: + return getattr(request, "param", None) + + def __repr__(self) -> str: + return f"" + + +def resolve_fixture_function( + fixturedef: FixtureDef[FixtureValue], request: FixtureRequest +) -> _FixtureFunc[FixtureValue]: + """Get the actual callable that can be called to obtain the fixture + value.""" + fixturefunc = fixturedef.func + # The fixture function needs to be bound to the actual + # request.instance so that code working with "fixturedef" behaves + # as expected. + instance = request.instance + if instance is not None: + # Handle the case where fixture is defined not in a test class, but some other class + # (for example a plugin class with a fixture), see #2270. + if hasattr(fixturefunc, "__self__") and not isinstance( + instance, + fixturefunc.__self__.__class__, + ): + return fixturefunc + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(instance) + return fixturefunc + + +def pytest_fixture_setup( + fixturedef: FixtureDef[FixtureValue], request: SubRequest +) -> FixtureValue: + """Execution of fixture setup.""" + kwargs = {} + for argname in fixturedef.argnames: + kwargs[argname] = request.getfixturevalue(argname) + + fixturefunc = resolve_fixture_function(fixturedef, request) + my_cache_key = fixturedef.cache_key(request) + + if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction( + fixturefunc + ): + auto_str = " with autouse=True" if fixturedef._autouse else "" + + warnings.warn( + PytestRemovedIn9Warning( + f"{request.node.name!r} requested an async fixture " + f"{request.fixturename!r}{auto_str}, with no plugin or hook that " + "handled it. This is usually an error, as pytest does not natively " + "support it. " + "This will turn into an error in pytest 9.\n" + "See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture" + ), + # no stacklevel will point at users code, so we just point here + stacklevel=1, + ) + + try: + result = call_fixture_func(fixturefunc, request, kwargs) + except TEST_OUTCOME as e: + if isinstance(e, skip.Exception): + # The test requested a fixture which caused a skip. + # Don't show the fixture as the skip location, as then the user + # wouldn't know which test skipped. + e._use_item_location = True + fixturedef.cached_result = (None, my_cache_key, (e, e.__traceback__)) + raise + fixturedef.cached_result = (result, my_cache_key, None) + return result + + +@final +@dataclasses.dataclass(frozen=True) +class FixtureFunctionMarker: + scope: _ScopeName | Callable[[str, Config], _ScopeName] + params: tuple[object, ...] | None + autouse: bool = False + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None + name: str | None = None + + _ispytest: dataclasses.InitVar[bool] = False + + def __post_init__(self, _ispytest: bool) -> None: + check_ispytest(_ispytest) + + def __call__(self, function: FixtureFunction) -> FixtureFunctionDefinition: + if inspect.isclass(function): + raise ValueError("class fixtures not supported (maybe in the future)") + + if isinstance(function, FixtureFunctionDefinition): + raise ValueError( + f"@pytest.fixture is being applied more than once to the same function {function.__name__!r}" + ) + + if hasattr(function, "pytestmark"): + warnings.warn(MARKED_FIXTURE, stacklevel=2) + + fixture_definition = FixtureFunctionDefinition( + function=function, fixture_function_marker=self, _ispytest=True + ) + + name = self.name or function.__name__ + if name == "request": + location = getlocation(function) + fail( + f"'request' is a reserved word for fixtures, use another name:\n {location}", + pytrace=False, + ) + + return fixture_definition + + +# TODO: paramspec/return type annotation tracking and storing +class FixtureFunctionDefinition: + def __init__( + self, + *, + function: Callable[..., Any], + fixture_function_marker: FixtureFunctionMarker, + instance: object | None = None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.name = fixture_function_marker.name or function.__name__ + # In order to show the function that this fixture contains in messages. + # Set the __name__ to be same as the function __name__ or the given fixture name. + self.__name__ = self.name + self._fixture_function_marker = fixture_function_marker + if instance is not None: + self._fixture_function = cast( + Callable[..., Any], function.__get__(instance) + ) + else: + self._fixture_function = function + functools.update_wrapper(self, function) + + def __repr__(self) -> str: + return f"" + + def __get__(self, instance, owner=None): + """Behave like a method if the function it was applied to was a method.""" + return FixtureFunctionDefinition( + function=self._fixture_function, + fixture_function_marker=self._fixture_function_marker, + instance=instance, + _ispytest=True, + ) + + def __call__(self, *args: Any, **kwds: Any) -> Any: + message = ( + f'Fixture "{self.name}" called directly. Fixtures are not meant to be called directly,\n' + "but are created automatically when test functions request them as parameters.\n" + "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly" + ) + fail(message, pytrace=False) + + def _get_wrapped_function(self) -> Callable[..., Any]: + return self._fixture_function + + +@overload +def fixture( + fixture_function: Callable[..., object], + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., + params: Iterable[object] | None = ..., + autouse: bool = ..., + ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., + name: str | None = ..., +) -> FixtureFunctionDefinition: ... + + +@overload +def fixture( + fixture_function: None = ..., + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., + params: Iterable[object] | None = ..., + autouse: bool = ..., + ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., + name: str | None = None, +) -> FixtureFunctionMarker: ... + + +def fixture( + fixture_function: FixtureFunction | None = None, + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = "function", + params: Iterable[object] | None = None, + autouse: bool = False, + ids: Sequence[object | None] | Callable[[Any], object | None] | None = None, + name: str | None = None, +) -> FixtureFunctionMarker | FixtureFunctionDefinition: + """Decorator to mark a fixture factory function. + + This decorator can be used, with or without parameters, to define a + fixture function. + + The name of the fixture function can later be referenced to cause its + invocation ahead of running tests: test modules or classes can use the + ``pytest.mark.usefixtures(fixturename)`` marker. + + Test functions can directly use fixture names as input arguments in which + case the fixture instance returned from the fixture function will be + injected. + + Fixtures can provide their values to test functions using ``return`` or + ``yield`` statements. When using ``yield`` the code block after the + ``yield`` statement is executed as teardown code regardless of the test + outcome, and must yield exactly once. + + :param scope: + The scope for which this fixture is shared; one of ``"function"`` + (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. + + This parameter may also be a callable which receives ``(fixture_name, config)`` + as parameters, and must return a ``str`` with one of the values mentioned above. + + See :ref:`dynamic scope` in the docs for more information. + + :param params: + An optional list of parameters which will cause multiple invocations + of the fixture function and all of the tests using it. The current + parameter is available in ``request.param``. + + :param autouse: + If True, the fixture func is activated for all tests that can see it. + If False (the default), an explicit reference is needed to activate + the fixture. + + :param ids: + Sequence of ids each corresponding to the params so that they are + part of the test id. If no ids are provided they will be generated + automatically from the params. + + :param name: + The name of the fixture. This defaults to the name of the decorated + function. If a fixture is used in the same module in which it is + defined, the function name of the fixture will be shadowed by the + function arg that requests the fixture; one way to resolve this is to + name the decorated function ``fixture_`` and then use + ``@pytest.fixture(name='')``. + """ + fixture_marker = FixtureFunctionMarker( + scope=scope, + params=tuple(params) if params is not None else None, + autouse=autouse, + ids=None if ids is None else ids if callable(ids) else tuple(ids), + name=name, + _ispytest=True, + ) + + # Direct decoration. + if fixture_function: + return fixture_marker(fixture_function) + + return fixture_marker + + +def yield_fixture( + fixture_function=None, + *args, + scope="function", + params=None, + autouse=False, + ids=None, + name=None, +): + """(Return a) decorator to mark a yield-fixture factory function. + + .. deprecated:: 3.0 + Use :py:func:`pytest.fixture` directly instead. + """ + warnings.warn(YIELD_FIXTURE, stacklevel=2) + return fixture( + fixture_function, + *args, + scope=scope, + params=params, + autouse=autouse, + ids=ids, + name=name, + ) + + +@fixture(scope="session") +def pytestconfig(request: FixtureRequest) -> Config: + """Session-scoped fixture that returns the session's :class:`pytest.Config` + object. + + Example:: + + def test_foo(pytestconfig): + if pytestconfig.get_verbosity() > 0: + ... + + """ + return request.config + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "usefixtures", + type="args", + default=[], + help="List of default fixtures to be used with this project", + ) + group = parser.getgroup("general") + group.addoption( + "--fixtures", + "--funcargs", + action="store_true", + dest="showfixtures", + default=False, + help="Show available fixtures, sorted by plugin appearance " + "(fixtures with leading '_' are only shown with '-v')", + ) + group.addoption( + "--fixtures-per-test", + action="store_true", + dest="show_fixtures_per_test", + default=False, + help="Show fixtures per test", + ) + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.showfixtures: + showfixtures(config) + return 0 + if config.option.show_fixtures_per_test: + show_fixtures_per_test(config) + return 0 + return None + + +def _get_direct_parametrize_args(node: nodes.Node) -> set[str]: + """Return all direct parametrization arguments of a node, so we don't + mistake them for fixtures. + + Check https://github.com/pytest-dev/pytest/issues/5036. + + These things are done later as well when dealing with parametrization + so this could be improved. + """ + parametrize_argnames: set[str] = set() + for marker in node.iter_markers(name="parametrize"): + if not marker.kwargs.get("indirect", False): + p_argnames, _ = ParameterSet._parse_parametrize_args( + *marker.args, **marker.kwargs + ) + parametrize_argnames.update(p_argnames) + return parametrize_argnames + + +def deduplicate_names(*seqs: Iterable[str]) -> tuple[str, ...]: + """De-duplicate the sequence of names while keeping the original order.""" + # Ideally we would use a set, but it does not preserve insertion order. + return tuple(dict.fromkeys(name for seq in seqs for name in seq)) + + +class FixtureManager: + """pytest fixture definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i.e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + + def __init__(self, session: Session) -> None: + self.session = session + self.config: Config = session.config + # Maps a fixture name (argname) to all of the FixtureDefs in the test + # suite/plugins defined with this name. Populated by parsefactories(). + # TODO: The order of the FixtureDefs list of each arg is significant, + # explain. + self._arg2fixturedefs: Final[dict[str, list[FixtureDef[Any]]]] = {} + self._holderobjseen: Final[set[object]] = set() + # A mapping from a nodeid to a list of autouse fixtures it defines. + self._nodeid_autousenames: Final[dict[str, list[str]]] = { + "": self.config.getini("usefixtures"), + } + session.config.pluginmanager.register(self, "funcmanage") + + def getfixtureinfo( + self, + node: nodes.Item, + func: Callable[..., object] | None, + cls: type | None, + ) -> FuncFixtureInfo: + """Calculate the :class:`FuncFixtureInfo` for an item. + + If ``func`` is None, or if the item sets an attribute + ``nofuncargs = True``, then ``func`` is not examined at all. + + :param node: + The item requesting the fixtures. + :param func: + The item's function. + :param cls: + If the function is a method, the method's class. + """ + if func is not None and not getattr(node, "nofuncargs", False): + argnames = getfuncargnames(func, name=node.name, cls=cls) + else: + argnames = () + usefixturesnames = self._getusefixturesnames(node) + autousenames = self._getautousenames(node) + initialnames = deduplicate_names(autousenames, usefixturesnames, argnames) + + direct_parametrize_args = _get_direct_parametrize_args(node) + + names_closure, arg2fixturedefs = self.getfixtureclosure( + parentnode=node, + initialnames=initialnames, + ignore_args=direct_parametrize_args, + ) + + return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin, plugin_name: str) -> None: + # Fixtures defined in conftest plugins are only visible to within the + # conftest's directory. This is unlike fixtures in non-conftest plugins + # which have global visibility. So for conftests, construct the base + # nodeid from the plugin name (which is the conftest path). + if plugin_name and plugin_name.endswith("conftest.py"): + # Note: we explicitly do *not* use `plugin.__file__` here -- The + # difference is that plugin_name has the correct capitalization on + # case-insensitive systems (Windows) and other normalization issues + # (issue #11816). + conftestpath = absolutepath(plugin_name) + try: + nodeid = str(conftestpath.parent.relative_to(self.config.rootpath)) + except ValueError: + nodeid = "" + if nodeid == ".": + nodeid = "" + if os.sep != nodes.SEP: + nodeid = nodeid.replace(os.sep, nodes.SEP) + else: + nodeid = None + + self.parsefactories(plugin, nodeid) + + def _getautousenames(self, node: nodes.Node) -> Iterator[str]: + """Return the names of autouse fixtures applicable to node.""" + for parentnode in node.listchain(): + basenames = self._nodeid_autousenames.get(parentnode.nodeid) + if basenames: + yield from basenames + + def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]: + """Return the names of usefixtures fixtures applicable to node.""" + for marker_node, mark in node.iter_markers_with_node(name="usefixtures"): + if not mark.args: + marker_node.warn( + PytestWarning( + f"usefixtures() in {node.nodeid} without arguments has no effect" + ) + ) + yield from mark.args + + def getfixtureclosure( + self, + parentnode: nodes.Node, + initialnames: tuple[str, ...], + ignore_args: AbstractSet[str], + ) -> tuple[list[str], dict[str, Sequence[FixtureDef[Any]]]]: + # Collect the closure of all fixtures, starting with the given + # fixturenames as the initial set. As we have to visit all + # factory definitions anyway, we also return an arg2fixturedefs + # mapping so that the caller can reuse it and does not have + # to re-discover fixturedefs again for each fixturename + # (discovering matching fixtures for a given name/node is expensive). + + fixturenames_closure = list(initialnames) + + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] = {} + lastlen = -1 + while lastlen != len(fixturenames_closure): + lastlen = len(fixturenames_closure) + for argname in fixturenames_closure: + if argname in ignore_args: + continue + if argname in arg2fixturedefs: + continue + fixturedefs = self.getfixturedefs(argname, parentnode) + if fixturedefs: + arg2fixturedefs[argname] = fixturedefs + for arg in fixturedefs[-1].argnames: + if arg not in fixturenames_closure: + fixturenames_closure.append(arg) + + def sort_by_scope(arg_name: str) -> Scope: + try: + fixturedefs = arg2fixturedefs[arg_name] + except KeyError: + return Scope.Function + else: + return fixturedefs[-1]._scope + + fixturenames_closure.sort(key=sort_by_scope, reverse=True) + return fixturenames_closure, arg2fixturedefs + + def pytest_generate_tests(self, metafunc: Metafunc) -> None: + """Generate new tests based on parametrized fixtures used by the given metafunc""" + + def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: + args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs) + return args + + for argname in metafunc.fixturenames: + # Get the FixtureDefs for the argname. + fixture_defs = metafunc._arg2fixturedefs.get(argname) + if not fixture_defs: + # Will raise FixtureLookupError at setup time if not parametrized somewhere + # else (e.g @pytest.mark.parametrize) + continue + + # If the test itself parametrizes using this argname, give it + # precedence. + if any( + argname in get_parametrize_mark_argnames(mark) + for mark in metafunc.definition.iter_markers("parametrize") + ): + continue + + # In the common case we only look at the fixture def with the + # closest scope (last in the list). But if the fixture overrides + # another fixture, while requesting the super fixture, keep going + # in case the super fixture is parametrized (#1953). + for fixturedef in reversed(fixture_defs): + # Fixture is parametrized, apply it and stop. + if fixturedef.params is not None: + metafunc.parametrize( + argname, + fixturedef.params, + indirect=True, + scope=fixturedef.scope, + ids=fixturedef.ids, + ) + break + + # Not requesting the overridden super fixture, stop. + if argname not in fixturedef.argnames: + break + + # Try next super fixture, if any. + + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> None: + # Separate parametrized setups. + items[:] = reorder_items(items) + + def _register_fixture( + self, + *, + name: str, + func: _FixtureFunc[object], + nodeid: str | None, + scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] = "function", + params: Sequence[object] | None = None, + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, + autouse: bool = False, + ) -> None: + """Register a fixture + + :param name: + The fixture's name. + :param func: + The fixture's implementation function. + :param nodeid: + The visibility of the fixture. The fixture will be available to the + node with this nodeid and its children in the collection tree. + None means that the fixture is visible to the entire collection tree, + e.g. a fixture defined for general use in a plugin. + :param scope: + The fixture's scope. + :param params: + The fixture's parametrization params. + :param ids: + The fixture's IDs. + :param autouse: + Whether this is an autouse fixture. + """ + fixture_def = FixtureDef( + config=self.config, + baseid=nodeid, + argname=name, + func=func, + scope=scope, + params=params, + ids=ids, + _ispytest=True, + _autouse=autouse, + ) + + faclist = self._arg2fixturedefs.setdefault(name, []) + if fixture_def.has_location: + faclist.append(fixture_def) + else: + # fixturedefs with no location are at the front + # so this inserts the current fixturedef after the + # existing fixturedefs from external plugins but + # before the fixturedefs provided in conftests. + i = len([f for f in faclist if not f.has_location]) + faclist.insert(i, fixture_def) + if autouse: + self._nodeid_autousenames.setdefault(nodeid or "", []).append(name) + + @overload + def parsefactories( + self, + node_or_obj: nodes.Node, + ) -> None: + raise NotImplementedError() + + @overload + def parsefactories( + self, + node_or_obj: object, + nodeid: str | None, + ) -> None: + raise NotImplementedError() + + def parsefactories( + self, + node_or_obj: nodes.Node | object, + nodeid: str | NotSetType | None = NOTSET, + ) -> None: + """Collect fixtures from a collection node or object. + + Found fixtures are parsed into `FixtureDef`s and saved. + + If `node_or_object` is a collection node (with an underlying Python + object), the node's object is traversed and the node's nodeid is used to + determine the fixtures' visibility. `nodeid` must not be specified in + this case. + + If `node_or_object` is an object (e.g. a plugin), the object is + traversed and the given `nodeid` is used to determine the fixtures' + visibility. `nodeid` must be specified in this case; None and "" mean + total visibility. + """ + if nodeid is not NOTSET: + holderobj = node_or_obj + else: + assert isinstance(node_or_obj, nodes.Node) + holderobj = cast(object, node_or_obj.obj) # type: ignore[attr-defined] + assert isinstance(node_or_obj.nodeid, str) + nodeid = node_or_obj.nodeid + if holderobj in self._holderobjseen: + return + + # Avoid accessing `@property` (and other descriptors) when iterating fixtures. + if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType): + holderobj_tp: object = type(holderobj) + else: + holderobj_tp = holderobj + + self._holderobjseen.add(holderobj) + for name in dir(holderobj): + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getattr() ignores such exceptions. + obj_ub = safe_getattr(holderobj_tp, name, None) + if type(obj_ub) is FixtureFunctionDefinition: + marker = obj_ub._fixture_function_marker + if marker.name: + fixture_name = marker.name + else: + fixture_name = name + + # OK we know it is a fixture -- now safe to look up on the _instance_. + try: + obj = getattr(holderobj, name) + # if the fixture is named in the decorator we cannot find it in the module + except AttributeError: + obj = obj_ub + + func = obj._get_wrapped_function() + + self._register_fixture( + name=fixture_name, + nodeid=nodeid, + func=func, + scope=marker.scope, + params=marker.params, + ids=marker.ids, + autouse=marker.autouse, + ) + + def getfixturedefs( + self, argname: str, node: nodes.Node + ) -> Sequence[FixtureDef[Any]] | None: + """Get FixtureDefs for a fixture name which are applicable + to a given node. + + Returns None if there are no fixtures at all defined with the given + name. (This is different from the case in which there are fixtures + with the given name, but none applicable to the node. In this case, + an empty result is returned). + + :param argname: Name of the fixture to search for. + :param node: The requesting Node. + """ + try: + fixturedefs = self._arg2fixturedefs[argname] + except KeyError: + return None + return tuple(self._matchfactories(fixturedefs, node)) + + def _matchfactories( + self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node + ) -> Iterator[FixtureDef[Any]]: + parentnodeids = {n.nodeid for n in node.iter_parents()} + for fixturedef in fixturedefs: + if fixturedef.baseid in parentnodeids: + yield fixturedef + + +def show_fixtures_per_test(config: Config) -> int | ExitCode: + from _pytest.main import wrap_session + + return wrap_session(config, _show_fixtures_per_test) + + +_PYTEST_DIR = Path(_pytest.__file__).parent + + +def _pretty_fixture_path(invocation_dir: Path, func) -> str: + loc = Path(getlocation(func, invocation_dir)) + prefix = Path("...", "_pytest") + try: + return str(prefix / loc.relative_to(_PYTEST_DIR)) + except ValueError: + return bestrelpath(invocation_dir, loc) + + +def _show_fixtures_per_test(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + invocation_dir = config.invocation_params.dir + tw = _pytest.config.create_terminal_writer(config) + verbose = config.get_verbosity() + + def get_best_relpath(func) -> str: + loc = getlocation(func, invocation_dir) + return bestrelpath(invocation_dir, Path(loc)) + + def write_fixture(fixture_def: FixtureDef[object]) -> None: + argname = fixture_def.argname + if verbose <= 0 and argname.startswith("_"): + return + prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func) + tw.write(f"{argname}", green=True) + tw.write(f" -- {prettypath}", yellow=True) + tw.write("\n") + fixture_doc = inspect.getdoc(fixture_def.func) + if fixture_doc: + write_docstring( + tw, + fixture_doc.split("\n\n", maxsplit=1)[0] + if verbose <= 0 + else fixture_doc, + ) + else: + tw.line(" no docstring available", red=True) + + def write_item(item: nodes.Item) -> None: + # Not all items have _fixtureinfo attribute. + info: FuncFixtureInfo | None = getattr(item, "_fixtureinfo", None) + if info is None or not info.name2fixturedefs: + # This test item does not use any fixtures. + return + tw.line() + tw.sep("-", f"fixtures used by {item.name}") + # TODO: Fix this type ignore. + tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] + # dict key not used in loop but needed for sorting. + for _, fixturedefs in sorted(info.name2fixturedefs.items()): + assert fixturedefs is not None + if not fixturedefs: + continue + # Last item is expected to be the one used by the test item. + write_fixture(fixturedefs[-1]) + + for session_item in session.items: + write_item(session_item) + + +def showfixtures(config: Config) -> int | ExitCode: + from _pytest.main import wrap_session + + return wrap_session(config, _showfixtures_main) + + +def _showfixtures_main(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + invocation_dir = config.invocation_params.dir + tw = _pytest.config.create_terminal_writer(config) + verbose = config.get_verbosity() + + fm = session._fixturemanager + + available = [] + seen: set[tuple[str, str]] = set() + + for argname, fixturedefs in fm._arg2fixturedefs.items(): + assert fixturedefs is not None + if not fixturedefs: + continue + for fixturedef in fixturedefs: + loc = getlocation(fixturedef.func, invocation_dir) + if (fixturedef.argname, loc) in seen: + continue + seen.add((fixturedef.argname, loc)) + available.append( + ( + len(fixturedef.baseid), + fixturedef.func.__module__, + _pretty_fixture_path(invocation_dir, fixturedef.func), + fixturedef.argname, + fixturedef, + ) + ) + + available.sort() + currentmodule = None + for baseid, module, prettypath, argname, fixturedef in available: + if currentmodule != module: + if not module.startswith("_pytest."): + tw.line() + tw.sep("-", f"fixtures defined from {module}") + currentmodule = module + if verbose <= 0 and argname.startswith("_"): + continue + tw.write(f"{argname}", green=True) + if fixturedef.scope != "function": + tw.write(f" [{fixturedef.scope} scope]", cyan=True) + tw.write(f" -- {prettypath}", yellow=True) + tw.write("\n") + doc = inspect.getdoc(fixturedef.func) + if doc: + write_docstring( + tw, doc.split("\n\n", maxsplit=1)[0] if verbose <= 0 else doc + ) + else: + tw.line(" no docstring available", red=True) + tw.line() + + +def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: + for line in doc.split("\n"): + tw.line(indent + line) diff --git a/.venv/lib/python3.11/site-packages/_pytest/freeze_support.py b/.venv/lib/python3.11/site-packages/_pytest/freeze_support.py new file mode 100644 index 00000000..959ff071 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/freeze_support.py @@ -0,0 +1,45 @@ +"""Provides a function to report all internal modules for using freezing +tools.""" + +from __future__ import annotations + +from collections.abc import Iterator +import types + + +def freeze_includes() -> list[str]: + """Return a list of module names used by pytest that should be + included by cx_freeze.""" + import _pytest + + result = list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules( + package: str | types.ModuleType, + prefix: str = "", +) -> Iterator[str]: + """Iterate over the names of all modules that can be found in the given + package, recursively. + + >>> import _pytest + >>> list(_iter_all_modules(_pytest)) + ['_pytest._argcomplete', '_pytest._code.code', ...] + """ + import os + import pkgutil + + if isinstance(package, str): + path = package + else: + # Type ignored because typeshed doesn't define ModuleType.__path__ + # (only defined on packages). + package_path = package.__path__ + path, prefix = package_path[0], package.__name__ + "." + for _, name, is_package in pkgutil.iter_modules([path]): + if is_package: + for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): + yield prefix + m + else: + yield prefix + name diff --git a/.venv/lib/python3.11/site-packages/_pytest/helpconfig.py b/.venv/lib/python3.11/site-packages/_pytest/helpconfig.py new file mode 100644 index 00000000..b5ac0e6a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/helpconfig.py @@ -0,0 +1,283 @@ +# mypy: allow-untyped-defs +"""Version info, help messages, tracing configuration.""" + +from __future__ import annotations + +from argparse import Action +from collections.abc import Generator +import os +import sys + +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import PrintHelp +from _pytest.config.argparsing import Parser +from _pytest.terminal import TerminalReporter +import pytest + + +class HelpAction(Action): + """An argparse Action that will raise an exception in order to skip the + rest of the argument parsing when --help is passed. + + This prevents argparse from quitting due to missing required arguments + when any are defined, for example by ``pytest_addoption``. + This is similar to the way that the builtin argparse --help option is + implemented by raising SystemExit. + """ + + def __init__(self, option_strings, dest=None, default=False, help=None): + super().__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + nargs=0, + help=help, + ) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + # We should only skip the rest of the parsing after preparse is done. + if getattr(parser._parser, "after_preparse", False): + raise PrintHelp + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--version", + "-V", + action="count", + default=0, + dest="version", + help="Display pytest version and information about plugins. " + "When given twice, also display information about plugins.", + ) + group._addoption( # private to use reserved lower-case short option + "-h", + "--help", + action=HelpAction, + dest="help", + help="Show help message and configuration info", + ) + group._addoption( # private to use reserved lower-case short option + "-p", + action="append", + dest="plugins", + default=[], + metavar="name", + help="Early-load given plugin module name or entry point (multi-allowed). " + "To avoid loading of plugins, use the `no:` prefix, e.g. " + "`no:doctest`. See also --disable-plugin-autoload.", + ) + group.addoption( + "--disable-plugin-autoload", + action="store_true", + default=False, + help="Disable plugin auto-loading through entry point packaging metadata. " + "Only plugins explicitly specified in -p or env var PYTEST_PLUGINS will be loaded.", + ) + group.addoption( + "--traceconfig", + "--trace-config", + action="store_true", + default=False, + help="Trace considerations of conftest.py files", + ) + group.addoption( + "--debug", + action="store", + nargs="?", + const="pytestdebug.log", + dest="debug", + metavar="DEBUG_FILE_NAME", + help="Store internal tracing debug information in this log file. " + "This file is opened with 'w' and truncated as a result, care advised. " + "Default: pytestdebug.log.", + ) + group._addoption( # private to use reserved lower-case short option + "-o", + "--override-ini", + dest="override_ini", + action="append", + help='Override ini option with "option=value" style, ' + "e.g. `-o xfail_strict=True -o cache_dir=cache`.", + ) + + +@pytest.hookimpl(wrapper=True) +def pytest_cmdline_parse() -> Generator[None, Config, Config]: + config = yield + + if config.option.debug: + # --debug | --debug was provided. + path = config.option.debug + debugfile = open(path, "w", encoding="utf-8") + debugfile.write( + "versions pytest-{}, " + "python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format( + pytest.__version__, + ".".join(map(str, sys.version_info)), + config.invocation_params.dir, + os.getcwd(), + config.invocation_params.args, + ) + ) + config.trace.root.setwriter(debugfile.write) + undo_tracing = config.pluginmanager.enable_tracing() + sys.stderr.write(f"writing pytest debug information to {path}\n") + + def unset_tracing() -> None: + debugfile.close() + sys.stderr.write(f"wrote pytest debug information to {debugfile.name}\n") + config.trace.root.setwriter(None) + undo_tracing() + + config.add_cleanup(unset_tracing) + + return config + + +def showversion(config: Config) -> None: + if config.option.version > 1: + sys.stdout.write( + f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n" + ) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stdout.write(line + "\n") + else: + sys.stdout.write(f"pytest {pytest.__version__}\n") + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.version > 0: + showversion(config) + return 0 + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return 0 + return None + + +def showhelp(config: Config) -> None: + import textwrap + + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + "terminalreporter" + ) + assert reporter is not None + tw = reporter._tw + tw.write(config._parser.optparser.format_help()) + tw.line() + tw.line( + "[pytest] ini-options in the first " + "pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:" + ) + tw.line() + + columns = tw.fullwidth # costly call + indent_len = 24 # based on argparse's max_help_position=24 + indent = " " * indent_len + for name in config._parser._ininames: + help, type, default = config._parser._inidict[name] + if type is None: + type = "string" + if help is None: + raise TypeError(f"help argument cannot be None for {name}") + spec = f"{name} ({type}):" + tw.write(f" {spec}") + spec_len = len(spec) + if spec_len > (indent_len - 3): + # Display help starting at a new line. + tw.line() + helplines = textwrap.wrap( + help, + columns, + initial_indent=indent, + subsequent_indent=indent, + break_on_hyphens=False, + ) + + for line in helplines: + tw.line(line) + else: + # Display help starting after the spec, following lines indented. + tw.write(" " * (indent_len - spec_len - 2)) + wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) + + if wrapped: + tw.line(wrapped[0]) + for line in wrapped[1:]: + tw.line(indent + line) + + tw.line() + tw.line("Environment variables:") + vars = [ + ( + "CI", + "When set (regardless of value), pytest knows it is running in a " + "CI process and does not truncate summary info", + ), + ("BUILD_NUMBER", "Equivalent to CI"), + ("PYTEST_ADDOPTS", "Extra command line options"), + ("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"), + ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"), + ("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"), + ] + for name, help in vars: + tw.line(f" {name:<24} {help}") + tw.line() + tw.line() + + tw.line("to see available markers type: pytest --markers") + tw.line("to see available fixtures type: pytest --fixtures") + tw.line( + "(shown according to specified file_or_dir or current dir " + "if not specified; fixtures with leading '_' are only shown " + "with the '-v' option" + ) + + for warningreport in reporter.stats.get("warnings", []): + tw.line("warning : " + warningreport.message, red=True) + + +conftest_options = [("pytest_plugins", "list of plugin names to load")] + + +def getpluginversioninfo(config: Config) -> list[str]: + lines = [] + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + lines.append("registered third-party plugins:") + for plugin, dist in plugininfo: + loc = getattr(plugin, "__file__", repr(plugin)) + content = f"{dist.project_name}-{dist.version} at {loc}" + lines.append(" " + content) + return lines + + +def pytest_report_header(config: Config) -> list[str]: + lines = [] + if config.option.debug or config.option.traceconfig: + lines.append(f"using: pytest-{pytest.__version__}") + + verinfo = getpluginversioninfo(config) + if verinfo: + lines.extend(verinfo) + + if config.option.traceconfig: + lines.append("active plugins:") + items = config.pluginmanager.list_name_plugin() + for name, plugin in items: + if hasattr(plugin, "__file__"): + r = plugin.__file__ + else: + r = repr(plugin) + lines.append(f" {name:<20}: {r}") + return lines diff --git a/.venv/lib/python3.11/site-packages/_pytest/hookspec.py b/.venv/lib/python3.11/site-packages/_pytest/hookspec.py new file mode 100644 index 00000000..12653ea1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/hookspec.py @@ -0,0 +1,1333 @@ +# mypy: allow-untyped-defs +# ruff: noqa: T100 +"""Hook specifications for pytest plugins which are invoked by pytest itself +and by builtin plugins.""" + +from __future__ import annotations + +from collections.abc import Mapping +from collections.abc import Sequence +from pathlib import Path +from typing import Any +from typing import TYPE_CHECKING + +from pluggy import HookspecMarker + +from .deprecated import HOOK_LEGACY_PATH_ARG + + +if TYPE_CHECKING: + import pdb + from typing import Literal + import warnings + + from _pytest._code.code import ExceptionInfo + from _pytest._code.code import ExceptionRepr + from _pytest.compat import LEGACY_PATH + from _pytest.config import _PluggyPlugin + from _pytest.config import Config + from _pytest.config import ExitCode + from _pytest.config import PytestPluginManager + from _pytest.config.argparsing import Parser + from _pytest.fixtures import FixtureDef + from _pytest.fixtures import SubRequest + from _pytest.main import Session + from _pytest.nodes import Collector + from _pytest.nodes import Item + from _pytest.outcomes import Exit + from _pytest.python import Class + from _pytest.python import Function + from _pytest.python import Metafunc + from _pytest.python import Module + from _pytest.reports import CollectReport + from _pytest.reports import TestReport + from _pytest.runner import CallInfo + from _pytest.terminal import TerminalReporter + from _pytest.terminal import TestShortLogReport + + +hookspec = HookspecMarker("pytest") + +# ------------------------------------------------------------------------- +# Initialization hooks called for every plugin +# ------------------------------------------------------------------------- + + +@hookspec(historic=True) +def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: + """Called at plugin registration time to allow adding new hooks via a call to + :func:`pluginmanager.add_hookspecs(module_or_class, prefix) `. + + :param pluginmanager: The pytest plugin manager. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered. + """ + + +@hookspec(historic=True) +def pytest_plugin_registered( + plugin: _PluggyPlugin, + plugin_name: str, + manager: PytestPluginManager, +) -> None: + """A new pytest plugin got registered. + + :param plugin: The plugin module or instance. + :param plugin_name: The name by which the plugin is registered. + :param manager: The pytest plugin manager. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered, once for each plugin registered thus far + (including itself!), and for all plugins thereafter when they are + registered. + """ + + +@hookspec(historic=True) +def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: + """Register argparse-style options and ini-style config values, + called once at the beginning of a test run. + + :param parser: + To add command line options, call + :py:func:`parser.addoption(...) `. + To add ini-file values call :py:func:`parser.addini(...) + `. + + :param pluginmanager: + The pytest plugin manager, which can be used to install :py:func:`~pytest.hookspec`'s + or :py:func:`~pytest.hookimpl`'s and allow one plugin to call another plugin's hooks + to change how command line options are added. + + Options can later be accessed through the + :py:class:`config ` object, respectively: + + - :py:func:`config.getoption(name) ` to + retrieve the value of a command line option. + + - :py:func:`config.getini(name) ` to retrieve + a value read from an ini-style file. + + The config object is passed around on many internal objects via the ``.config`` + attribute or can be retrieved as the ``pytestconfig`` fixture. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered. + + This hook is only called for :ref:`initial conftests `. + """ + + +@hookspec(historic=True) +def pytest_configure(config: Config) -> None: + """Allow plugins and conftest files to perform initial configuration. + + .. note:: + This hook is incompatible with hook wrappers. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + This hook is called for every :ref:`initial conftest ` file + after command line options have been parsed. After that, the hook is called + for other conftest files as they are registered. + """ + + +# ------------------------------------------------------------------------- +# Bootstrapping hooks called for plugins registered early enough: +# internal and 3rd party plugins. +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_cmdline_parse( + pluginmanager: PytestPluginManager, args: list[str] +) -> Config | None: + """Return an initialized :class:`~pytest.Config`, parsing the specified args. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + This hook is only called for plugin classes passed to the + ``plugins`` arg when using `pytest.main`_ to perform an in-process + test run. + + :param pluginmanager: The pytest plugin manager. + :param args: List of arguments passed on the command line. + :returns: A pytest config object. + + Use in conftest plugins + ======================= + + This hook is not called for conftest files. + """ + + +def pytest_load_initial_conftests( + early_config: Config, parser: Parser, args: list[str] +) -> None: + """Called to implement the loading of :ref:`initial conftest files + ` ahead of command line option parsing. + + :param early_config: The pytest config object. + :param args: Arguments passed on the command line. + :param parser: To add command line options. + + Use in conftest plugins + ======================= + + This hook is not called for conftest files. + """ + + +@hookspec(firstresult=True) +def pytest_cmdline_main(config: Config) -> ExitCode | int | None: + """Called for performing the main command line action. + + The default implementation will invoke the configure hooks and + :hook:`pytest_runtestloop`. + + Stops at first non-None result, see :ref:`firstresult`. + + :param config: The pytest config object. + :returns: The exit code. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +# ------------------------------------------------------------------------- +# collection hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_collection(session: Session) -> object | None: + """Perform the collection phase for the given session. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + The default collection phase is this (see individual hooks for full details): + + 1. Starting from ``session`` as the initial collector: + + 1. ``pytest_collectstart(collector)`` + 2. ``report = pytest_make_collect_report(collector)`` + 3. ``pytest_exception_interact(collector, call, report)`` if an interactive exception occurred + 4. For each collected node: + + 1. If an item, ``pytest_itemcollected(item)`` + 2. If a collector, recurse into it. + + 5. ``pytest_collectreport(report)`` + + 2. ``pytest_collection_modifyitems(session, config, items)`` + + 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) + + 3. ``pytest_collection_finish(session)`` + 4. Set ``session.items`` to the list of collected items + 5. Set ``session.testscollected`` to the number of collected items + + You can implement this hook to only perform some action before collection, + for example the terminal plugin uses it to start displaying the collection + counter (and returns `None`). + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +def pytest_collection_modifyitems( + session: Session, config: Config, items: list[Item] +) -> None: + """Called after collection has been performed. May filter or re-order + the items in-place. + + When items are deselected (filtered out from ``items``), + the hook :hook:`pytest_deselected` must be called explicitly + with the deselected items to properly notify other plugins, + e.g. with ``config.hook.pytest_deselected(items=deselected_items)``. + + :param session: The pytest session object. + :param config: The pytest config object. + :param items: List of item objects. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_collection_finish(session: Session) -> None: + """Called after collection has been performed and modified. + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec( + firstresult=True, + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="collection_path" + ), + }, +) +def pytest_ignore_collect( + collection_path: Path, path: LEGACY_PATH, config: Config +) -> bool | None: + """Return ``True`` to ignore this path for collection. + + Return ``None`` to let other plugins ignore the path for collection. + + Returning ``False`` will forcefully *not* ignore this path for collection, + without giving a chance for other plugins to ignore this path. + + This hook is consulted for all files and directories prior to calling + more specific hooks. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collection_path: The path to analyze. + :type collection_path: pathlib.Path + :param path: The path to analyze (deprecated). + :param config: The pytest config object. + + .. versionchanged:: 7.0.0 + The ``collection_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. The ``path`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collection path, only + conftest files in parent directories of the collection path are consulted + (if the path is a directory, its own conftest file is *not* consulted - a + directory cannot ignore itself!). + """ + + +@hookspec(firstresult=True) +def pytest_collect_directory(path: Path, parent: Collector) -> Collector | None: + """Create a :class:`~pytest.Collector` for the given directory, or None if + not relevant. + + .. versionadded:: 8.0 + + For best results, the returned collector should be a subclass of + :class:`~pytest.Directory`, but this is not required. + + The new node needs to have the specified ``parent`` as a parent. + + Stops at first non-None result, see :ref:`firstresult`. + + :param path: The path to analyze. + :type path: pathlib.Path + + See :ref:`custom directory collectors` for a simple example of use of this + hook. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collection path, only + conftest files in parent directories of the collection path are consulted + (if the path is a directory, its own conftest file is *not* consulted - a + directory cannot collect itself!). + """ + + +@hookspec( + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="file_path" + ), + }, +) +def pytest_collect_file( + file_path: Path, path: LEGACY_PATH, parent: Collector +) -> Collector | None: + """Create a :class:`~pytest.Collector` for the given path, or None if not relevant. + + For best results, the returned collector should be a subclass of + :class:`~pytest.File`, but this is not required. + + The new node needs to have the specified ``parent`` as a parent. + + :param file_path: The path to analyze. + :type file_path: pathlib.Path + :param path: The path to collect (deprecated). + + .. versionchanged:: 7.0.0 + The ``file_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. The ``path`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given file path, only + conftest files in parent directories of the file path are consulted. + """ + + +# logging hooks for collection + + +def pytest_collectstart(collector: Collector) -> None: + """Collector starts collecting. + + :param collector: + The collector. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +def pytest_itemcollected(item: Item) -> None: + """We just collected a test item. + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_collectreport(report: CollectReport) -> None: + """Collector finished collecting. + + :param report: + The collect report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +def pytest_deselected(items: Sequence[Item]) -> None: + """Called for deselected test items, e.g. by keyword. + + Note that this hook has two integration aspects for plugins: + + - it can be *implemented* to be notified of deselected items + - it must be *called* from :hook:`pytest_collection_modifyitems` + implementations when items are deselected (to properly notify other plugins). + + May be called multiple times. + + :param items: + The items. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_make_collect_report(collector: Collector) -> CollectReport | None: + """Perform :func:`collector.collect() ` and return + a :class:`~pytest.CollectReport`. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collector: + The collector. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +# ------------------------------------------------------------------------- +# Python test function related hooks +# ------------------------------------------------------------------------- + + +@hookspec( + firstresult=True, + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="module_path" + ), + }, +) +def pytest_pycollect_makemodule( + module_path: Path, path: LEGACY_PATH, parent +) -> Module | None: + """Return a :class:`pytest.Module` collector or None for the given path. + + This hook will be called for each matching test module path. + The :hook:`pytest_collect_file` hook needs to be used if you want to + create test modules for files that do not match as a test module. + + Stops at first non-None result, see :ref:`firstresult`. + + :param module_path: The path of the module to collect. + :type module_path: pathlib.Path + :param path: The path of the module to collect (deprecated). + + .. versionchanged:: 7.0.0 + The ``module_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. + + The ``path`` parameter has been deprecated in favor of ``fspath``. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given parent collector, + only conftest files in the collector's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> None | Item | Collector | list[Item | Collector]: + """Return a custom item/collector for a Python object in a module, or None. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collector: + The module/class collector. + :param name: + The name of the object in the module/class. + :param obj: + The object. + :returns: + The created items/collectors. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: + """Call underlying test function. + + Stops at first non-None result, see :ref:`firstresult`. + + :param pyfuncitem: + The function item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only + conftest files in the item's directory and its parent directories + are consulted. + """ + + +def pytest_generate_tests(metafunc: Metafunc) -> None: + """Generate (multiple) parametrized calls to a test function. + + :param metafunc: + The :class:`~pytest.Metafunc` helper for the test function. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given function definition, + only conftest files in the functions's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_make_parametrize_id(config: Config, val: object, argname: str) -> str | None: + """Return a user-friendly string representation of the given ``val`` + that will be used by @pytest.mark.parametrize calls, or None if the hook + doesn't know about ``val``. + + The parameter name is available as ``argname``, if required. + + Stops at first non-None result, see :ref:`firstresult`. + + :param config: The pytest config object. + :param val: The parametrized value. + :param argname: The automatic parameter name produced by pytest. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +# ------------------------------------------------------------------------- +# runtest related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_runtestloop(session: Session) -> object | None: + """Perform the main runtest loop (after collection finished). + + The default hook implementation performs the runtest protocol for all items + collected in the session (``session.items``), unless the collection failed + or the ``collectonly`` pytest option is set. + + If at any point :py:func:`pytest.exit` is called, the loop is + terminated immediately. + + If at any point ``session.shouldfail`` or ``session.shouldstop`` are set, the + loop is terminated after the runtest protocol for the current item is finished. + + :param session: The pytest session object. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> object | None: + """Perform the runtest protocol for a single test item. + + The default runtest protocol is this (see individual hooks for full details): + + - ``pytest_runtest_logstart(nodeid, location)`` + + - Setup phase: + - ``call = pytest_runtest_setup(item)`` (wrapped in ``CallInfo(when="setup")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Call phase, if the setup passed and the ``setuponly`` pytest option is not set: + - ``call = pytest_runtest_call(item)`` (wrapped in ``CallInfo(when="call")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Teardown phase: + - ``call = pytest_runtest_teardown(item, nextitem)`` (wrapped in ``CallInfo(when="teardown")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - ``pytest_runtest_logfinish(nodeid, location)`` + + :param item: Test item for which the runtest protocol is performed. + :param nextitem: The scheduled-to-be-next test item (or None if this is the end my friend). + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +def pytest_runtest_logstart(nodeid: str, location: tuple[str, int | None, str]) -> None: + """Called at the start of running the runtest protocol for a single item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)`` + where ``filename`` is a file path relative to ``config.rootpath`` + and ``lineno`` is 0-based. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_logfinish( + nodeid: str, location: tuple[str, int | None, str] +) -> None: + """Called at the end of running the runtest protocol for a single item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)`` + where ``filename`` is a file path relative to ``config.rootpath`` + and ``lineno`` is 0-based. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_setup(item: Item) -> None: + """Called to perform the setup phase for a test item. + + The default implementation runs ``setup()`` on ``item`` and all of its + parents (which haven't been setup yet). This includes obtaining the + values of fixtures required by the item (which haven't been obtained + yet). + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_call(item: Item) -> None: + """Called to run the test for test item (the call phase). + + The default implementation calls ``item.runtest()``. + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + """Called to perform the teardown phase for a test item. + + The default implementation runs the finalizers and calls ``teardown()`` + on ``item`` and all of its parents (which need to be torn down). This + includes running the teardown phase of fixtures required by the item (if + they go out of scope). + + :param item: + The item. + :param nextitem: + The scheduled-to-be-next test item (None if no further test item is + scheduled). This argument is used to perform exact teardowns, i.e. + calling just enough finalizers so that nextitem only needs to call + setup functions. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport | None: + """Called to create a :class:`~pytest.TestReport` for each of + the setup, call and teardown runtest phases of a test item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param item: The item. + :param call: The :class:`~pytest.CallInfo` for the phase. + + Stops at first non-None result, see :ref:`firstresult`. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_logreport(report: TestReport) -> None: + """Process the :class:`~pytest.TestReport` produced for each + of the setup, call and teardown runtest phases of an item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_report_to_serializable( + config: Config, + report: CollectReport | TestReport, +) -> dict[str, Any] | None: + """Serialize the given report object into a data structure suitable for + sending over the wire, e.g. converted to JSON. + + :param config: The pytest config object. + :param report: The report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. The exact details may depend + on the plugin which calls the hook. + """ + + +@hookspec(firstresult=True) +def pytest_report_from_serializable( + config: Config, + data: dict[str, Any], +) -> CollectReport | TestReport | None: + """Restore a report object previously serialized with + :hook:`pytest_report_to_serializable`. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. The exact details may depend + on the plugin which calls the hook. + """ + + +# ------------------------------------------------------------------------- +# Fixture related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[Any], request: SubRequest +) -> object | None: + """Perform fixture setup execution. + + :param fixturedef: + The fixture definition object. + :param request: + The fixture request object. + :returns: + The return value of the call to the fixture function. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + If the fixture function returns None, other implementations of + this hook function will continue to be called, according to the + behavior of the :ref:`firstresult` option. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given fixture, only + conftest files in the fixture scope's directory and its parent directories + are consulted. + """ + + +def pytest_fixture_post_finalizer( + fixturedef: FixtureDef[Any], request: SubRequest +) -> None: + """Called after fixture teardown, but before the cache is cleared, so + the fixture result ``fixturedef.cached_result`` is still available (not + ``None``). + + :param fixturedef: + The fixture definition object. + :param request: + The fixture request object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given fixture, only + conftest files in the fixture scope's directory and its parent directories + are consulted. + """ + + +# ------------------------------------------------------------------------- +# test session related hooks +# ------------------------------------------------------------------------- + + +def pytest_sessionstart(session: Session) -> None: + """Called after the ``Session`` object has been created and before performing collection + and entering the run test loop. + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +def pytest_sessionfinish( + session: Session, + exitstatus: int | ExitCode, +) -> None: + """Called after whole test run finished, right before returning the exit status to the system. + + :param session: The pytest session object. + :param exitstatus: The status which pytest will return to the system. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +def pytest_unconfigure(config: Config) -> None: + """Called before test process is exited. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +# ------------------------------------------------------------------------- +# hooks for customizing the assert methods +# ------------------------------------------------------------------------- + + +def pytest_assertrepr_compare( + config: Config, op: str, left: object, right: object +) -> list[str] | None: + """Return explanation for comparisons in failing assert expressions. + + Return None for no custom explanation, otherwise return a list + of strings. The strings will be joined by newlines but any newlines + *in* a string will be escaped. Note that all but the first line will + be indented slightly, the intention is for the first line to be a summary. + + :param config: The pytest config object. + :param op: The operator, e.g. `"=="`, `"!="`, `"not in"`. + :param left: The left operand. + :param right: The right operand. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_assertion_pass(item: Item, lineno: int, orig: str, expl: str) -> None: + """Called whenever an assertion passes. + + .. versionadded:: 5.0 + + Use this hook to do some processing after a passing assertion. + The original assertion information is available in the `orig` string + and the pytest introspected assertion information is available in the + `expl` string. + + This hook must be explicitly enabled by the ``enable_assertion_pass_hook`` + ini-file option: + + .. code-block:: ini + + [pytest] + enable_assertion_pass_hook=true + + You need to **clean the .pyc** files in your project directory and interpreter libraries + when enabling this option, as assertions will require to be re-written. + + :param item: pytest item object of current test. + :param lineno: Line number of the assert statement. + :param orig: String with the original assertion. + :param expl: String with the assert explanation. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing reporting (invoked from _pytest_terminal). +# ------------------------------------------------------------------------- + + +@hookspec( + warn_on_impl_args={ + "startdir": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="startdir", pathlib_path_arg="start_path" + ), + }, +) +def pytest_report_header( # type:ignore[empty-body] + config: Config, start_path: Path, startdir: LEGACY_PATH +) -> str | list[str]: + """Return a string or list of strings to be displayed as header info for terminal reporting. + + :param config: The pytest config object. + :param start_path: The starting dir. + :type start_path: pathlib.Path + :param startdir: The starting dir (deprecated). + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + + .. versionchanged:: 7.0.0 + The ``start_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. The ``startdir`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +@hookspec( + warn_on_impl_args={ + "startdir": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="startdir", pathlib_path_arg="start_path" + ), + }, +) +def pytest_report_collectionfinish( # type:ignore[empty-body] + config: Config, + start_path: Path, + startdir: LEGACY_PATH, + items: Sequence[Item], +) -> str | list[str]: + """Return a string or list of strings to be displayed after collection + has finished successfully. + + These strings will be displayed after the standard "collected X items" message. + + .. versionadded:: 3.2 + + :param config: The pytest config object. + :param start_path: The starting dir. + :type start_path: pathlib.Path + :param startdir: The starting dir (deprecated). + :param items: List of pytest items that are going to be executed; this list should not be modified. + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + + .. versionchanged:: 7.0.0 + The ``start_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. The ``startdir`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_report_teststatus( # type:ignore[empty-body] + report: CollectReport | TestReport, config: Config +) -> TestShortLogReport | tuple[str, str, str | tuple[str, Mapping[str, bool]]]: + """Return result-category, shortletter and verbose word for status + reporting. + + The result-category is a category in which to count the result, for + example "passed", "skipped", "error" or the empty string. + + The shortletter is shown as testing progresses, for example ".", "s", + "E" or the empty string. + + The verbose word is shown as testing progresses in verbose mode, for + example "PASSED", "SKIPPED", "ERROR" or the empty string. + + pytest may style these implicitly according to the report outcome. + To provide explicit styling, return a tuple for the verbose word, + for example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :param report: The report object whose status is to be returned. + :param config: The pytest config object. + :returns: The test status. + + Stops at first non-None result, see :ref:`firstresult`. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_terminal_summary( + terminalreporter: TerminalReporter, + exitstatus: ExitCode, + config: Config, +) -> None: + """Add a section to terminal summary reporting. + + :param terminalreporter: The internal terminal reporter object. + :param exitstatus: The exit status that will be reported back to the OS. + :param config: The pytest config object. + + .. versionadded:: 4.2 + The ``config`` parameter. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec(historic=True) +def pytest_warning_recorded( + warning_message: warnings.WarningMessage, + when: Literal["config", "collect", "runtest"], + nodeid: str, + location: tuple[str, int, str] | None, +) -> None: + """Process a warning captured by the internal pytest warnings plugin. + + :param warning_message: + The captured warning. This is the same object produced by :class:`warnings.catch_warnings`, + and contains the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param nodeid: + Full id of the item. Empty string for warnings that are not specific to + a particular node. + + :param location: + When available, holds information about the execution context of the captured + warning (filename, linenumber, function). ``function`` evaluates to + when the execution context is at the module level. + + .. versionadded:: 6.0 + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. If the warning is specific to a + particular node, only conftest files in parent directories of the node are + consulted. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing skipping +# ------------------------------------------------------------------------- + + +def pytest_markeval_namespace( # type:ignore[empty-body] + config: Config, +) -> dict[str, Any]: + """Called when constructing the globals dictionary used for + evaluating string conditions in xfail/skipif markers. + + This is useful when the condition for a marker requires + objects that are expensive or impossible to obtain during + collection time, which is required by normal boolean + conditions. + + .. versionadded:: 6.2 + + :param config: The pytest config object. + :returns: A dictionary of additional globals to add. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in parent directories of the item are consulted. + """ + + +# ------------------------------------------------------------------------- +# error handling and internal debugging hooks +# ------------------------------------------------------------------------- + + +def pytest_internalerror( + excrepr: ExceptionRepr, + excinfo: ExceptionInfo[BaseException], +) -> bool | None: + """Called for internal errors. + + Return True to suppress the fallback handling of printing an + INTERNALERROR message directly to sys.stderr. + + :param excrepr: The exception repr object. + :param excinfo: The exception info. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_keyboard_interrupt( + excinfo: ExceptionInfo[KeyboardInterrupt | Exit], +) -> None: + """Called for keyboard interrupt. + + :param excinfo: The exception info. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_exception_interact( + node: Item | Collector, + call: CallInfo[Any], + report: CollectReport | TestReport, +) -> None: + """Called when an exception was raised which can potentially be + interactively handled. + + May be called during collection (see :hook:`pytest_make_collect_report`), + in which case ``report`` is a :class:`~pytest.CollectReport`. + + May be called during runtest of an item (see :hook:`pytest_runtest_protocol`), + in which case ``report`` is a :class:`~pytest.TestReport`. + + This hook is not called if the exception that was raised is an internal + exception like ``skip.Exception``. + + :param node: + The item or collector. + :param call: + The call information. Contains the exception. + :param report: + The collection or test report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given node, only conftest + files in parent directories of the node are consulted. + """ + + +def pytest_enter_pdb(config: Config, pdb: pdb.Pdb) -> None: + """Called upon pdb.set_trace(). + + Can be used by plugins to take special action just before the python + debugger enters interactive mode. + + :param config: The pytest config object. + :param pdb: The Pdb instance. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_leave_pdb(config: Config, pdb: pdb.Pdb) -> None: + """Called when leaving pdb (e.g. with continue after pdb.set_trace()). + + Can be used by plugins to take special action just after the python + debugger leaves interactive mode. + + :param config: The pytest config object. + :param pdb: The Pdb instance. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ diff --git a/.venv/lib/python3.11/site-packages/_pytest/junitxml.py b/.venv/lib/python3.11/site-packages/_pytest/junitxml.py new file mode 100644 index 00000000..dc35e3aa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/junitxml.py @@ -0,0 +1,692 @@ +# mypy: allow-untyped-defs +"""Report test results in JUnit-XML format, for use with Jenkins and build +integration servers. + +Based on initial code from Ross Lawley. + +Output conforms to +https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd +""" + +from __future__ import annotations + +from collections.abc import Callable +import functools +import os +import platform +import re +import xml.etree.ElementTree as ET + +from _pytest import nodes +from _pytest import timing +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprFileLocation +from _pytest.config import Config +from _pytest.config import filename_arg +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureRequest +from _pytest.reports import TestReport +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter +import pytest + + +xml_key = StashKey["LogXML"]() + + +def bin_xml_escape(arg: object) -> str: + r"""Visually escape invalid XML characters. + + For example, transforms + 'hello\aworld\b' + into + 'hello#x07world#x08' + Note that the #xABs are *not* XML escapes - missing the ampersand «. + The idea is to escape visually for the user rather than for XML itself. + """ + + def repl(matchobj: re.Match[str]) -> str: + i = ord(matchobj.group()) + if i <= 0xFF: + return f"#x{i:02X}" + else: + return f"#x{i:04X}" + + # The spec range of valid chars is: + # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + # For an unknown(?) reason, we disallow #x7F (DEL) as well. + illegal_xml_re = ( + "[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\u10000-\u10ffff]" + ) + return re.sub(illegal_xml_re, repl, str(arg)) + + +def merge_family(left, right) -> None: + result = {} + for kl, vl in left.items(): + for kr, vr in right.items(): + if not isinstance(vl, list): + raise TypeError(type(vl)) + result[kl] = vl + vr + left.update(result) + + +families = { # pylint: disable=dict-init-mutate + "_base": {"testcase": ["classname", "name"]}, + "_base_legacy": {"testcase": ["file", "line", "url"]}, +} +# xUnit 1.x inherits legacy attributes. +families["xunit1"] = families["_base"].copy() +merge_family(families["xunit1"], families["_base_legacy"]) + +# xUnit 2.x uses strict base attributes. +families["xunit2"] = families["_base"] + + +class _NodeReporter: + def __init__(self, nodeid: str | TestReport, xml: LogXML) -> None: + self.id = nodeid + self.xml = xml + self.add_stats = self.xml.add_stats + self.family = self.xml.family + self.duration = 0.0 + self.properties: list[tuple[str, str]] = [] + self.nodes: list[ET.Element] = [] + self.attrs: dict[str, str] = {} + + def append(self, node: ET.Element) -> None: + self.xml.add_stats(node.tag) + self.nodes.append(node) + + def add_property(self, name: str, value: object) -> None: + self.properties.append((str(name), bin_xml_escape(value))) + + def add_attribute(self, name: str, value: object) -> None: + self.attrs[str(name)] = bin_xml_escape(value) + + def make_properties_node(self) -> ET.Element | None: + """Return a Junit node containing custom properties, if any.""" + if self.properties: + properties = ET.Element("properties") + for name, value in self.properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None + + def record_testreport(self, testreport: TestReport) -> None: + names = mangle_test_address(testreport.nodeid) + existing_attrs = self.attrs + classnames = names[:-1] + if self.xml.prefix: + classnames.insert(0, self.xml.prefix) + attrs: dict[str, str] = { + "classname": ".".join(classnames), + "name": bin_xml_escape(names[-1]), + "file": testreport.location[0], + } + if testreport.location[1] is not None: + attrs["line"] = str(testreport.location[1]) + if hasattr(testreport, "url"): + attrs["url"] = testreport.url + self.attrs = attrs + self.attrs.update(existing_attrs) # Restore any user-defined attributes. + + # Preserve legacy testcase behavior. + if self.family == "xunit1": + return + + # Filter out attributes not permitted by this test family. + # Including custom attributes because they are not valid here. + temp_attrs = {} + for key in self.attrs: + if key in families[self.family]["testcase"]: + temp_attrs[key] = self.attrs[key] + self.attrs = temp_attrs + + def to_xml(self) -> ET.Element: + testcase = ET.Element("testcase", self.attrs, time=f"{self.duration:.3f}") + properties = self.make_properties_node() + if properties is not None: + testcase.append(properties) + testcase.extend(self.nodes) + return testcase + + def _add_simple(self, tag: str, message: str, data: str | None = None) -> None: + node = ET.Element(tag, message=message) + node.text = bin_xml_escape(data) + self.append(node) + + def write_captured_output(self, report: TestReport) -> None: + if not self.xml.log_passing_tests and report.passed: + return + + content_out = report.capstdout + content_log = report.caplog + content_err = report.capstderr + if self.xml.logging == "no": + return + content_all = "" + if self.xml.logging in ["log", "all"]: + content_all = self._prepare_content(content_log, " Captured Log ") + if self.xml.logging in ["system-out", "out-err", "all"]: + content_all += self._prepare_content(content_out, " Captured Out ") + self._write_content(report, content_all, "system-out") + content_all = "" + if self.xml.logging in ["system-err", "out-err", "all"]: + content_all += self._prepare_content(content_err, " Captured Err ") + self._write_content(report, content_all, "system-err") + content_all = "" + if content_all: + self._write_content(report, content_all, "system-out") + + def _prepare_content(self, content: str, header: str) -> str: + return "\n".join([header.center(80, "-"), content, ""]) + + def _write_content(self, report: TestReport, content: str, jheader: str) -> None: + tag = ET.Element(jheader) + tag.text = bin_xml_escape(content) + self.append(tag) + + def append_pass(self, report: TestReport) -> None: + self.add_stats("passed") + + def append_failure(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + if hasattr(report, "wasxfail"): + self._add_simple("skipped", "xfail-marked test passes unexpectedly") + else: + assert report.longrepr is not None + reprcrash: ReprFileLocation | None = getattr( + report.longrepr, "reprcrash", None + ) + if reprcrash is not None: + message = reprcrash.message + else: + message = str(report.longrepr) + message = bin_xml_escape(message) + self._add_simple("failure", message, str(report.longrepr)) + + def append_collect_error(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + assert report.longrepr is not None + self._add_simple("error", "collection failure", str(report.longrepr)) + + def append_collect_skipped(self, report: TestReport) -> None: + self._add_simple("skipped", "collection skipped", str(report.longrepr)) + + def append_error(self, report: TestReport) -> None: + assert report.longrepr is not None + reprcrash: ReprFileLocation | None = getattr(report.longrepr, "reprcrash", None) + if reprcrash is not None: + reason = reprcrash.message + else: + reason = str(report.longrepr) + + if report.when == "teardown": + msg = f'failed on teardown with "{reason}"' + else: + msg = f'failed on setup with "{reason}"' + self._add_simple("error", bin_xml_escape(msg), str(report.longrepr)) + + def append_skipped(self, report: TestReport) -> None: + if hasattr(report, "wasxfail"): + xfailreason = report.wasxfail + if xfailreason.startswith("reason: "): + xfailreason = xfailreason[8:] + xfailreason = bin_xml_escape(xfailreason) + skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason) + self.append(skipped) + else: + assert isinstance(report.longrepr, tuple) + filename, lineno, skipreason = report.longrepr + if skipreason.startswith("Skipped: "): + skipreason = skipreason[9:] + details = f"{filename}:{lineno}: {skipreason}" + + skipped = ET.Element( + "skipped", type="pytest.skip", message=bin_xml_escape(skipreason) + ) + skipped.text = bin_xml_escape(details) + self.append(skipped) + self.write_captured_output(report) + + def finalize(self) -> None: + data = self.to_xml() + self.__dict__.clear() + # Type ignored because mypy doesn't like overriding a method. + # Also the return value doesn't match... + self.to_xml = lambda: data # type: ignore[method-assign] + + +def _warn_incompatibility_with_xunit2( + request: FixtureRequest, fixture_name: str +) -> None: + """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" + from _pytest.warning_types import PytestWarning + + xml = request.config.stash.get(xml_key, None) + if xml is not None and xml.family not in ("xunit1", "legacy"): + request.node.warn( + PytestWarning( + f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')" + ) + ) + + +@pytest.fixture +def record_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra properties to the calling test. + + User properties become part of the test report and are available to the + configured reporters, like JUnit XML. + + The fixture is callable with ``name, value``. The value is automatically + XML-encoded. + + Example:: + + def test_function(record_property): + record_property("example_key", 1) + """ + _warn_incompatibility_with_xunit2(request, "record_property") + + def append_property(name: str, value: object) -> None: + request.node.user_properties.append((name, value)) + + return append_property + + +@pytest.fixture +def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra xml attributes to the tag for the calling test. + + The fixture is callable with ``name, value``. The value is + automatically XML-encoded. + """ + from _pytest.warning_types import PytestExperimentalApiWarning + + request.node.warn( + PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + ) + + _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + + # Declare noop + def add_attr_noop(name: str, value: object) -> None: + pass + + attr_func = add_attr_noop + + xml = request.config.stash.get(xml_key, None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + attr_func = node_reporter.add_attribute + + return attr_func + + +def _check_record_param_type(param: str, v: str) -> None: + """Used by record_testsuite_property to check that the given parameter name is of the proper + type.""" + __tracebackhide__ = True + if not isinstance(v, str): + msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable] + raise TypeError(msg.format(param=param, g=type(v).__name__)) + + +@pytest.fixture(scope="session") +def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Record a new ```` tag as child of the root ````. + + This is suitable to writing global information regarding the entire test + suite, and is compatible with ``xunit2`` JUnit family. + + This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: + + .. code-block:: python + + def test_foo(record_testsuite_property): + record_testsuite_property("ARCH", "PPC") + record_testsuite_property("STORAGE_TYPE", "CEPH") + + :param name: + The property name. + :param value: + The property value. Will be converted to a string. + + .. warning:: + + Currently this fixture **does not work** with the + `pytest-xdist `__ plugin. See + :issue:`7767` for details. + """ + __tracebackhide__ = True + + def record_func(name: str, value: object) -> None: + """No-op function in case --junit-xml was not passed in the command-line.""" + __tracebackhide__ = True + _check_record_param_type("name", name) + + xml = request.config.stash.get(xml_key, None) + if xml is not None: + record_func = xml.add_global_property + return record_func + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--junitxml", + "--junit-xml", + action="store", + dest="xmlpath", + metavar="path", + type=functools.partial(filename_arg, optname="--junitxml"), + default=None, + help="Create junit-xml style report file at given path", + ) + group.addoption( + "--junitprefix", + "--junit-prefix", + action="store", + metavar="str", + default=None, + help="Prepend prefix to classnames in junit-xml output", + ) + parser.addini( + "junit_suite_name", "Test suite name for JUnit report", default="pytest" + ) + parser.addini( + "junit_logging", + "Write captured log messages to JUnit report: " + "one of no|log|system-out|system-err|out-err|all", + default="no", + ) + parser.addini( + "junit_log_passing_tests", + "Capture log information for passing tests to JUnit report: ", + type="bool", + default=True, + ) + parser.addini( + "junit_duration_report", + "Duration time to report: one of total|call", + default="total", + ) # choices=['total', 'call']) + parser.addini( + "junit_family", + "Emit XML for schema: one of legacy|xunit1|xunit2", + default="xunit2", + ) + + +def pytest_configure(config: Config) -> None: + xmlpath = config.option.xmlpath + # Prevent opening xmllog on worker nodes (xdist). + if xmlpath and not hasattr(config, "workerinput"): + junit_family = config.getini("junit_family") + config.stash[xml_key] = LogXML( + xmlpath, + config.option.junitprefix, + config.getini("junit_suite_name"), + config.getini("junit_logging"), + config.getini("junit_duration_report"), + junit_family, + config.getini("junit_log_passing_tests"), + ) + config.pluginmanager.register(config.stash[xml_key]) + + +def pytest_unconfigure(config: Config) -> None: + xml = config.stash.get(xml_key, None) + if xml: + del config.stash[xml_key] + config.pluginmanager.unregister(xml) + + +def mangle_test_address(address: str) -> list[str]: + path, possible_open_bracket, params = address.partition("[") + names = path.split("::") + # Convert file path to dotted path. + names[0] = names[0].replace(nodes.SEP, ".") + names[0] = re.sub(r"\.py$", "", names[0]) + # Put any params back. + names[-1] += possible_open_bracket + params + return names + + +class LogXML: + def __init__( + self, + logfile, + prefix: str | None, + suite_name: str = "pytest", + logging: str = "no", + report_duration: str = "total", + family="xunit1", + log_passing_tests: bool = True, + ) -> None: + logfile = os.path.expanduser(os.path.expandvars(logfile)) + self.logfile = os.path.normpath(os.path.abspath(logfile)) + self.prefix = prefix + self.suite_name = suite_name + self.logging = logging + self.log_passing_tests = log_passing_tests + self.report_duration = report_duration + self.family = family + self.stats: dict[str, int] = dict.fromkeys( + ["error", "passed", "failure", "skipped"], 0 + ) + self.node_reporters: dict[tuple[str | TestReport, object], _NodeReporter] = {} + self.node_reporters_ordered: list[_NodeReporter] = [] + self.global_properties: list[tuple[str, str]] = [] + + # List of reports that failed on call but teardown is pending. + self.open_reports: list[TestReport] = [] + self.cnt_double_fail_tests = 0 + + # Replaces convenience family with real family. + if self.family == "legacy": + self.family = "xunit1" + + def finalize(self, report: TestReport) -> None: + nodeid = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + reporter = self.node_reporters.pop((nodeid, workernode)) + + for propname, propvalue in report.user_properties: + reporter.add_property(propname, str(propvalue)) + + if reporter is not None: + reporter.finalize() + + def node_reporter(self, report: TestReport | str) -> _NodeReporter: + nodeid: str | TestReport = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + + key = nodeid, workernode + + if key in self.node_reporters: + # TODO: breaks for --dist=each + return self.node_reporters[key] + + reporter = _NodeReporter(nodeid, self) + + self.node_reporters[key] = reporter + self.node_reporters_ordered.append(reporter) + + return reporter + + def add_stats(self, key: str) -> None: + if key in self.stats: + self.stats[key] += 1 + + def _opentestcase(self, report: TestReport) -> _NodeReporter: + reporter = self.node_reporter(report) + reporter.record_testreport(report) + return reporter + + def pytest_runtest_logreport(self, report: TestReport) -> None: + """Handle a setup/call/teardown report, generating the appropriate + XML tags as necessary. + + Note: due to plugins like xdist, this hook may be called in interlaced + order with reports from other nodes. For example: + + Usual call order: + -> setup node1 + -> call node1 + -> teardown node1 + -> setup node2 + -> call node2 + -> teardown node2 + + Possible call order in xdist: + -> setup node1 + -> call node1 + -> setup node2 + -> call node2 + -> teardown node2 + -> teardown node1 + """ + close_report = None + if report.passed: + if report.when == "call": # ignore setup/teardown + reporter = self._opentestcase(report) + reporter.append_pass(report) + elif report.failed: + if report.when == "teardown": + # The following vars are needed when xdist plugin is used. + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + # We need to open new testcase in case we have failure in + # call and error in teardown in order to follow junit + # schema. + self.finalize(close_report) + self.cnt_double_fail_tests += 1 + reporter = self._opentestcase(report) + if report.when == "call": + reporter.append_failure(report) + self.open_reports.append(report) + if not self.log_passing_tests: + reporter.write_captured_output(report) + else: + reporter.append_error(report) + elif report.skipped: + reporter = self._opentestcase(report) + reporter.append_skipped(report) + self.update_testcase_duration(report) + if report.when == "teardown": + reporter = self._opentestcase(report) + reporter.write_captured_output(report) + + self.finalize(report) + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + self.open_reports.remove(close_report) + + def update_testcase_duration(self, report: TestReport) -> None: + """Accumulate total duration for nodeid from given report and update + the Junit.testcase with the new total if already created.""" + if self.report_duration in {"total", report.when}: + reporter = self.node_reporter(report) + reporter.duration += getattr(report, "duration", 0.0) + + def pytest_collectreport(self, report: TestReport) -> None: + if not report.passed: + reporter = self._opentestcase(report) + if report.failed: + reporter.append_collect_error(report) + else: + reporter.append_collect_skipped(report) + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: + reporter = self.node_reporter("internal") + reporter.attrs.update(classname="pytest", name="internal") + reporter._add_simple("error", "internal error", str(excrepr)) + + def pytest_sessionstart(self) -> None: + self.suite_start = timing.Instant() + + def pytest_sessionfinish(self) -> None: + dirname = os.path.dirname(os.path.abspath(self.logfile)) + # exist_ok avoids filesystem race conditions between checking path existence and requesting creation + os.makedirs(dirname, exist_ok=True) + + with open(self.logfile, "w", encoding="utf-8") as logfile: + duration = self.suite_start.elapsed() + + numtests = ( + self.stats["passed"] + + self.stats["failure"] + + self.stats["skipped"] + + self.stats["error"] + - self.cnt_double_fail_tests + ) + logfile.write('') + + suite_node = ET.Element( + "testsuite", + name=self.suite_name, + errors=str(self.stats["error"]), + failures=str(self.stats["failure"]), + skipped=str(self.stats["skipped"]), + tests=str(numtests), + time=f"{duration.seconds:.3f}", + timestamp=self.suite_start.as_utc().astimezone().isoformat(), + hostname=platform.node(), + ) + global_properties = self._get_global_properties_node() + if global_properties is not None: + suite_node.append(global_properties) + for node_reporter in self.node_reporters_ordered: + suite_node.append(node_reporter.to_xml()) + testsuites = ET.Element("testsuites") + testsuites.set("name", "pytest tests") + testsuites.append(suite_node) + logfile.write(ET.tostring(testsuites, encoding="unicode")) + + def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: + terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") + + def add_global_property(self, name: str, value: object) -> None: + __tracebackhide__ = True + _check_record_param_type("name", name) + self.global_properties.append((name, bin_xml_escape(value))) + + def _get_global_properties_node(self) -> ET.Element | None: + """Return a Junit node containing custom properties, if any.""" + if self.global_properties: + properties = ET.Element("properties") + for name, value in self.global_properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None diff --git a/.venv/lib/python3.11/site-packages/_pytest/legacypath.py b/.venv/lib/python3.11/site-packages/_pytest/legacypath.py new file mode 100644 index 00000000..59e8ef6e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/legacypath.py @@ -0,0 +1,468 @@ +# mypy: allow-untyped-defs +"""Add backward compatibility support for the legacy py path type.""" + +from __future__ import annotations + +import dataclasses +from pathlib import Path +import shlex +import subprocess +from typing import Final +from typing import final +from typing import TYPE_CHECKING + +from iniconfig import SectionWrapper + +from _pytest.cacheprovider import Cache +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pytester import HookRecorder +from _pytest.pytester import Pytester +from _pytest.pytester import RunResult +from _pytest.terminal import TerminalReporter +from _pytest.tmpdir import TempPathFactory + + +if TYPE_CHECKING: + import pexpect + + +@final +class Testdir: + """ + Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. + + All methods just forward to an internal :class:`Pytester` instance, converting results + to `legacy_path` objects as necessary. + """ + + __test__ = False + + CLOSE_STDIN: Final = Pytester.CLOSE_STDIN + TimeoutExpired: Final = Pytester.TimeoutExpired + + def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pytester = pytester + + @property + def tmpdir(self) -> LEGACY_PATH: + """Temporary directory where tests are executed.""" + return legacy_path(self._pytester.path) + + @property + def test_tmproot(self) -> LEGACY_PATH: + return legacy_path(self._pytester._test_tmproot) + + @property + def request(self): + return self._pytester._request + + @property + def plugins(self): + return self._pytester.plugins + + @plugins.setter + def plugins(self, plugins): + self._pytester.plugins = plugins + + @property + def monkeypatch(self) -> MonkeyPatch: + return self._pytester._monkeypatch + + def make_hook_recorder(self, pluginmanager) -> HookRecorder: + """See :meth:`Pytester.make_hook_recorder`.""" + return self._pytester.make_hook_recorder(pluginmanager) + + def chdir(self) -> None: + """See :meth:`Pytester.chdir`.""" + return self._pytester.chdir() + + def finalize(self) -> None: + return self._pytester._finalize() + + def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makefile`.""" + if ext and not ext.startswith("."): + # pytester.makefile is going to throw a ValueError in a way that + # testdir.makefile did not, because + # pathlib.Path is stricter suffixes than py.path + # This ext arguments is likely user error, but since testdir has + # allowed this, we will prepend "." as a workaround to avoid breaking + # testdir usage that worked before + ext = "." + ext + return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) + + def makeconftest(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeconftest`.""" + return legacy_path(self._pytester.makeconftest(source)) + + def makeini(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeini`.""" + return legacy_path(self._pytester.makeini(source)) + + def getinicfg(self, source: str) -> SectionWrapper: + """See :meth:`Pytester.getinicfg`.""" + return self._pytester.getinicfg(source) + + def makepyprojecttoml(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makepyprojecttoml`.""" + return legacy_path(self._pytester.makepyprojecttoml(source)) + + def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makepyfile`.""" + return legacy_path(self._pytester.makepyfile(*args, **kwargs)) + + def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.maketxtfile`.""" + return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) + + def syspathinsert(self, path=None) -> None: + """See :meth:`Pytester.syspathinsert`.""" + return self._pytester.syspathinsert(path) + + def mkdir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkdir`.""" + return legacy_path(self._pytester.mkdir(name)) + + def mkpydir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkpydir`.""" + return legacy_path(self._pytester.mkpydir(name)) + + def copy_example(self, name=None) -> LEGACY_PATH: + """See :meth:`Pytester.copy_example`.""" + return legacy_path(self._pytester.copy_example(name)) + + def getnode(self, config: Config, arg) -> Item | Collector | None: + """See :meth:`Pytester.getnode`.""" + return self._pytester.getnode(config, arg) + + def getpathnode(self, path): + """See :meth:`Pytester.getpathnode`.""" + return self._pytester.getpathnode(path) + + def genitems(self, colitems: list[Item | Collector]) -> list[Item]: + """See :meth:`Pytester.genitems`.""" + return self._pytester.genitems(colitems) + + def runitem(self, source): + """See :meth:`Pytester.runitem`.""" + return self._pytester.runitem(source) + + def inline_runsource(self, source, *cmdlineargs): + """See :meth:`Pytester.inline_runsource`.""" + return self._pytester.inline_runsource(source, *cmdlineargs) + + def inline_genitems(self, *args): + """See :meth:`Pytester.inline_genitems`.""" + return self._pytester.inline_genitems(*args) + + def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): + """See :meth:`Pytester.inline_run`.""" + return self._pytester.inline_run( + *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc + ) + + def runpytest_inprocess(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest_inprocess`.""" + return self._pytester.runpytest_inprocess(*args, **kwargs) + + def runpytest(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest`.""" + return self._pytester.runpytest(*args, **kwargs) + + def parseconfig(self, *args) -> Config: + """See :meth:`Pytester.parseconfig`.""" + return self._pytester.parseconfig(*args) + + def parseconfigure(self, *args) -> Config: + """See :meth:`Pytester.parseconfigure`.""" + return self._pytester.parseconfigure(*args) + + def getitem(self, source, funcname="test_func"): + """See :meth:`Pytester.getitem`.""" + return self._pytester.getitem(source, funcname) + + def getitems(self, source): + """See :meth:`Pytester.getitems`.""" + return self._pytester.getitems(source) + + def getmodulecol(self, source, configargs=(), withinit=False): + """See :meth:`Pytester.getmodulecol`.""" + return self._pytester.getmodulecol( + source, configargs=configargs, withinit=withinit + ) + + def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: + """See :meth:`Pytester.collect_by_name`.""" + return self._pytester.collect_by_name(modcol, name) + + def popen( + self, + cmdargs, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """See :meth:`Pytester.popen`.""" + return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) + + def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: + """See :meth:`Pytester.run`.""" + return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) + + def runpython(self, script) -> RunResult: + """See :meth:`Pytester.runpython`.""" + return self._pytester.runpython(script) + + def runpython_c(self, command): + """See :meth:`Pytester.runpython_c`.""" + return self._pytester.runpython_c(command) + + def runpytest_subprocess(self, *args, timeout=None) -> RunResult: + """See :meth:`Pytester.runpytest_subprocess`.""" + return self._pytester.runpytest_subprocess(*args, timeout=timeout) + + def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn_pytest`.""" + return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn`.""" + return self._pytester.spawn(cmd, expect_timeout=expect_timeout) + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return str(self.tmpdir) + + +class LegacyTestdirPlugin: + @staticmethod + @fixture + def testdir(pytester: Pytester) -> Testdir: + """ + Identical to :fixture:`pytester`, and provides an instance whose methods return + legacy ``LEGACY_PATH`` objects instead when applicable. + + New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. + """ + return Testdir(pytester, _ispytest=True) + + +@final +@dataclasses.dataclass +class TempdirFactory: + """Backward compatibility wrapper that implements ``py.path.local`` + for :class:`TempPathFactory`. + + .. note:: + These days, it is preferred to use ``tmp_path_factory``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + """ + + _tmppath_factory: TempPathFactory + + def __init__( + self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._tmppath_factory = tmppath_factory + + def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) + + def getbasetemp(self) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.getbasetemp().resolve()) + + +class LegacyTmpdirPlugin: + @staticmethod + @fixture(scope="session") + def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: + """Return a :class:`pytest.TempdirFactory` instance for the test session.""" + # Set dynamically by pytest_configure(). + return request.config._tmpdirhandler # type: ignore + + @staticmethod + @fixture + def tmpdir(tmp_path: Path) -> LEGACY_PATH: + """Return a temporary directory (as `legacy_path`_ object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + + .. note:: + These days, it is preferred to use ``tmp_path``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + .. _legacy_path: https://py.readthedocs.io/en/latest/path.html + """ + return legacy_path(tmp_path) + + +def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH: + """Return a directory path object with the given name. + + Same as :func:`mkdir`, but returns a legacy py path instance. + """ + return legacy_path(self.mkdir(name)) + + +def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH: + """(deprecated) The file system path of the test module which collected this test.""" + return legacy_path(self.path) + + +def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config_invocation_dir(self: Config) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use :attr:`invocation_params.dir `, + which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.invocation_params.dir)) + + +def Config_rootdir(self: Config) -> LEGACY_PATH: + """The path to the :ref:`rootdir `. + + Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.rootpath)) + + +def Config_inifile(self: Config) -> LEGACY_PATH | None: + """The path to the :ref:`configfile `. + + Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. + + :type: Optional[LEGACY_PATH] + """ + return legacy_path(str(self.inipath)) if self.inipath else None + + +def Session_startdir(self: Session) -> LEGACY_PATH: + """The path from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config__getini_unknown_type(self, name: str, type: str, value: str | list[str]): + if type == "pathlist": + # TODO: This assert is probably not valid in all cases. + assert self.inipath is not None + dp = self.inipath.parent + input_values = shlex.split(value) if isinstance(value, str) else value + return [legacy_path(str(dp / x)) for x in input_values] + else: + raise ValueError(f"unknown configuration type: {type}", value) + + +def Node_fspath(self: Node) -> LEGACY_PATH: + """(deprecated) returns a legacy_path copy of self.path""" + return legacy_path(self.path) + + +def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: + self.path = Path(value) + + +@hookimpl(tryfirst=True) +def pytest_load_initial_conftests(early_config: Config) -> None: + """Monkeypatch legacy path attributes in several classes, as early as possible.""" + mp = MonkeyPatch() + early_config.add_cleanup(mp.undo) + + # Add Cache.makedir(). + mp.setattr(Cache, "makedir", Cache_makedir, raising=False) + + # Add FixtureRequest.fspath property. + mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) + + # Add TerminalReporter.startdir property. + mp.setattr( + TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False + ) + + # Add Config.{invocation_dir,rootdir,inifile} properties. + mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) + mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) + mp.setattr(Config, "inifile", property(Config_inifile), raising=False) + + # Add Session.startdir property. + mp.setattr(Session, "startdir", property(Session_startdir), raising=False) + + # Add pathlist configuration type. + mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) + + # Add Node.fspath property. + mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) + + +@hookimpl +def pytest_configure(config: Config) -> None: + """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" + if config.pluginmanager.has_plugin("tmpdir"): + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + # Create TmpdirFactory and attach it to the config object. + # + # This is to comply with existing plugins which expect the handler to be + # available at pytest_configure time, but ideally should be moved entirely + # to the tmpdir_factory session fixture. + try: + tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] + except AttributeError: + # tmpdir plugin is blocked. + pass + else: + _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) + mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) + + config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + + +@hookimpl +def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: + # pytester is not loaded by default and is commonly loaded from a conftest, + # so checking for it in `pytest_configure` is not enough. + is_pytester = plugin is manager.get_plugin("pytester") + if is_pytester and not manager.is_registered(LegacyTestdirPlugin): + manager.register(LegacyTestdirPlugin, "legacypath-pytester") diff --git a/.venv/lib/python3.11/site-packages/_pytest/logging.py b/.venv/lib/python3.11/site-packages/_pytest/logging.py new file mode 100644 index 00000000..e4fed579 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/logging.py @@ -0,0 +1,960 @@ +# mypy: allow-untyped-defs +"""Access and control log capturing.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Set as AbstractSet +from contextlib import contextmanager +from contextlib import nullcontext +from datetime import datetime +from datetime import timedelta +from datetime import timezone +import io +from io import StringIO +import logging +from logging import LogRecord +import os +from pathlib import Path +import re +from types import TracebackType +from typing import final +from typing import Generic +from typing import Literal +from typing import TYPE_CHECKING +from typing import TypeVar + +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.capture import CaptureManager +from _pytest.config import _strtobool +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter + + +if TYPE_CHECKING: + logging_StreamHandler = logging.StreamHandler[StringIO] +else: + logging_StreamHandler = logging.StreamHandler + +DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" +DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +caplog_handler_key = StashKey["LogCaptureHandler"]() +caplog_records_key = StashKey[dict[str, list[logging.LogRecord]]]() + + +def _remove_ansi_escape_sequences(text: str) -> str: + return _ANSI_ESCAPE_SEQ.sub("", text) + + +class DatetimeFormatter(logging.Formatter): + """A logging formatter which formats record with + :func:`datetime.datetime.strftime` formatter instead of + :func:`time.strftime` in case of microseconds in format string. + """ + + def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: + if datefmt and "%f" in datefmt: + ct = self.converter(record.created) + tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone) + # Construct `datetime.datetime` object from `struct_time` + # and msecs information from `record` + # Using int() instead of round() to avoid it exceeding 1_000_000 and causing a ValueError (#11861). + dt = datetime(*ct[0:6], microsecond=int(record.msecs * 1000), tzinfo=tz) + return dt.strftime(datefmt) + # Use `logging.Formatter` for non-microsecond formats + return super().formatTime(record, datefmt) + + +class ColoredLevelFormatter(DatetimeFormatter): + """A logging formatter which colorizes the %(levelname)..s part of the + log format passed to __init__.""" + + LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { + logging.CRITICAL: {"red"}, + logging.ERROR: {"red", "bold"}, + logging.WARNING: {"yellow"}, + logging.WARN: {"yellow"}, + logging.INFO: {"green"}, + logging.DEBUG: {"purple"}, + logging.NOTSET: set(), + } + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") + + def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._terminalwriter = terminalwriter + self._original_fmt = self._style._fmt + self._level_to_fmt_mapping: dict[int, str] = {} + + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + self.add_color_level(level, *color_opts) + + def add_color_level(self, level: int, *color_opts: str) -> None: + """Add or update color opts for a log level. + + :param level: + Log level to apply a style to, e.g. ``logging.INFO``. + :param color_opts: + ANSI escape sequence color options. Capitalized colors indicates + background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold + green text on yellow background. + + .. warning:: + This is an experimental API. + """ + assert self._fmt is not None + levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) + if not levelname_fmt_match: + return + levelname_fmt = levelname_fmt_match.group() + + formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} + + # add ANSI escape sequences around the formatted levelname + color_kwargs = {name: True for name in color_opts} + colorized_formatted_levelname = self._terminalwriter.markup( + formatted_levelname, **color_kwargs + ) + self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( + colorized_formatted_levelname, self._fmt + ) + + def format(self, record: logging.LogRecord) -> str: + fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) + self._style._fmt = fmt + return super().format(record) + + +class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. + + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ + + def __init__(self, fmt: str, auto_indent: int | str | bool | None) -> None: + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + + @staticmethod + def _get_auto_indent(auto_indent_option: int | str | bool | None) -> int: + """Determine the current auto indentation setting. + + Specify auto indent behavior (on/off/fixed) by passing in + extra={"auto_indent": [value]} to the call to logging.log() or + using a --log-auto-indent [value] command line or the + log_auto_indent [value] config option. + + Default behavior is auto-indent off. + + Using the string "True" or "on" or the boolean True as the value + turns auto indent on, using the string "False" or "off" or the + boolean False or the int 0 turns it off, and specifying a + positive integer fixes the indentation position to the value + specified. + + Any other values for the option are invalid, and will silently be + converted to the default. + + :param None|bool|int|str auto_indent_option: + User specified option for indentation from command line, config + or extra kwarg. Accepts int, bool or str. str option accepts the + same range of values as boolean config options, as well as + positive integers represented in str form. + + :returns: + Indentation value, which can be + -1 (automatically determine indentation) or + 0 (auto-indent turned off) or + >0 (explicitly set indentation position). + """ + if auto_indent_option is None: + return 0 + elif isinstance(auto_indent_option, bool): + if auto_indent_option: + return -1 + else: + return 0 + elif isinstance(auto_indent_option, int): + return int(auto_indent_option) + elif isinstance(auto_indent_option, str): + try: + return int(auto_indent_option) + except ValueError: + pass + try: + if _strtobool(auto_indent_option): + return -1 + except ValueError: + return 0 + + return 0 + + def format(self, record: logging.LogRecord) -> str: + if "\n" in record.message: + if hasattr(record, "auto_indent"): + # Passed in from the "extra={}" kwarg on the call to logging.log(). + auto_indent = self._get_auto_indent(record.auto_indent) + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % {**record.__dict__, "message": lines[0]} + + if auto_indent < 0: + indentation = _remove_ansi_escape_sequences(formatted).find( + lines[0] + ) + else: + # Optimizes logging by allowing a fixed indentation. + indentation = auto_indent + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + return self._fmt % record.__dict__ + + +def get_option_ini(config: Config, *names: str): + for name in names: + ret = config.getoption(name) # 'default' arg won't work as expected + if ret is None: + ret = config.getini(name) + if ret: + return ret + + +def pytest_addoption(parser: Parser) -> None: + """Add options to control log capturing.""" + group = parser.getgroup("logging") + + def add_option_ini(option, dest, default=None, type=None, **kwargs): + parser.addini( + dest, default=default, type=type, help="Default value for " + option + ) + group.addoption(option, dest=dest, **kwargs) + + add_option_ini( + "--log-level", + dest="log_level", + default=None, + metavar="LEVEL", + help=( + "Level of messages to catch/display." + " Not set by default, so it depends on the root/parent log handler's" + ' effective level, where it is "WARNING" by default.' + ), + ) + add_option_ini( + "--log-format", + dest="log_format", + default=DEFAULT_LOG_FORMAT, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-date-format", + dest="log_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="Log date format used by the logging module", + ) + parser.addini( + "log_cli", + default=False, + type="bool", + help='Enable log display during test run (also known as "live logging")', + ) + add_option_ini( + "--log-cli-level", dest="log_cli_level", default=None, help="CLI logging level" + ) + add_option_ini( + "--log-cli-format", + dest="log_cli_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-cli-date-format", + dest="log_cli_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-file", + dest="log_file", + default=None, + help="Path to a file when logging will be written to", + ) + add_option_ini( + "--log-file-mode", + dest="log_file_mode", + default="w", + choices=["w", "a"], + help="Log file open mode", + ) + add_option_ini( + "--log-file-level", + dest="log_file_level", + default=None, + help="Log file logging level", + ) + add_option_ini( + "--log-file-format", + dest="log_file_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-file-date-format", + dest="log_file_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-auto-indent", + dest="log_auto_indent", + default=None, + help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + ) + group.addoption( + "--log-disable", + action="append", + default=[], + dest="logger_disable", + help="Disable a logger by name. Can be passed multiple times.", + ) + + +_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) + + +# Not using @contextmanager for performance reasons. +class catching_logs(Generic[_HandlerType]): + """Context manager that prepares the whole logging machinery properly.""" + + __slots__ = ("handler", "level", "orig_level") + + def __init__(self, handler: _HandlerType, level: int | None = None) -> None: + self.handler = handler + self.level = level + + def __enter__(self) -> _HandlerType: + root_logger = logging.getLogger() + if self.level is not None: + self.handler.setLevel(self.level) + root_logger.addHandler(self.handler) + if self.level is not None: + self.orig_level = root_logger.level + root_logger.setLevel(min(self.orig_level, self.level)) + return self.handler + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + root_logger = logging.getLogger() + if self.level is not None: + root_logger.setLevel(self.orig_level) + root_logger.removeHandler(self.handler) + + +class LogCaptureHandler(logging_StreamHandler): + """A logging handler that stores log records and the log text.""" + + def __init__(self) -> None: + """Create a new log handler.""" + super().__init__(StringIO()) + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + """Keep the log records in a list in addition to the log text.""" + self.records.append(record) + super().emit(record) + + def reset(self) -> None: + self.records = [] + self.stream = StringIO() + + def clear(self) -> None: + self.records.clear() + self.stream = StringIO() + + def handleError(self, record: logging.LogRecord) -> None: + if logging.raiseExceptions: + # Fail the test if the log message is bad (emit failed). + # The default behavior of logging is to print "Logging error" + # to stderr with the call stack and some extra details. + # pytest wants to make such mistakes visible during testing. + raise # noqa: PLE0704 + + +@final +class LogCaptureFixture: + """Provides access and control of log capturing.""" + + def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._item = item + self._initial_handler_level: int | None = None + # Dict of log name -> log level. + self._initial_logger_levels: dict[str | None, int] = {} + self._initial_disabled_logging_level: int | None = None + + def _finalize(self) -> None: + """Finalize the fixture. + + This restores the log levels and the disabled logging levels changed by :meth:`set_level`. + """ + # Restore log levels. + if self._initial_handler_level is not None: + self.handler.setLevel(self._initial_handler_level) + for logger_name, level in self._initial_logger_levels.items(): + logger = logging.getLogger(logger_name) + logger.setLevel(level) + # Disable logging at the original disabled logging level. + if self._initial_disabled_logging_level is not None: + logging.disable(self._initial_disabled_logging_level) + self._initial_disabled_logging_level = None + + @property + def handler(self) -> LogCaptureHandler: + """Get the logging handler used by the fixture.""" + return self._item.stash[caplog_handler_key] + + def get_records( + self, when: Literal["setup", "call", "teardown"] + ) -> list[logging.LogRecord]: + """Get the logging records for one of the possible test phases. + + :param when: + Which test phase to obtain the records from. + Valid values are: "setup", "call" and "teardown". + + :returns: The list of captured records at the given stage. + + .. versionadded:: 3.4 + """ + return self._item.stash[caplog_records_key].get(when, []) + + @property + def text(self) -> str: + """The formatted log text.""" + return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) + + @property + def records(self) -> list[logging.LogRecord]: + """The list of log records.""" + return self.handler.records + + @property + def record_tuples(self) -> list[tuple[str, int, str]]: + """A list of a stripped down version of log records intended + for use in assertion comparison. + + The format of the tuple is: + + (logger_name, log_level, message) + """ + return [(r.name, r.levelno, r.getMessage()) for r in self.records] + + @property + def messages(self) -> list[str]: + """A list of format-interpolated log messages. + + Unlike 'records', which contains the format string and parameters for + interpolation, log messages in this list are all interpolated. + + Unlike 'text', which contains the output from the handler, log + messages in this list are unadorned with levels, timestamps, etc, + making exact comparisons more reliable. + + Note that traceback or stack info (from :func:`logging.exception` or + the `exc_info` or `stack_info` arguments to the logging functions) is + not included, as this is added by the formatter in the handler. + + .. versionadded:: 3.7 + """ + return [r.getMessage() for r in self.records] + + def clear(self) -> None: + """Reset the list of log records and the captured log text.""" + self.handler.clear() + + def _force_enable_logging( + self, level: int | str, logger_obj: logging.Logger + ) -> int: + """Enable the desired logging level if the global level was disabled via ``logging.disabled``. + + Only enables logging levels greater than or equal to the requested ``level``. + + Does nothing if the desired ``level`` wasn't disabled. + + :param level: + The logger level caplog should capture. + All logging is enabled if a non-standard logging level string is supplied. + Valid level strings are in :data:`logging._nameToLevel`. + :param logger_obj: The logger object to check. + + :return: The original disabled logging level. + """ + original_disable_level: int = logger_obj.manager.disable + + if isinstance(level, str): + # Try to translate the level string to an int for `logging.disable()` + level = logging.getLevelName(level) + + if not isinstance(level, int): + # The level provided was not valid, so just un-disable all logging. + logging.disable(logging.NOTSET) + elif not logger_obj.isEnabledFor(level): + # Each level is `10` away from other levels. + # https://docs.python.org/3/library/logging.html#logging-levels + disable_level = max(level - 10, logging.NOTSET) + logging.disable(disable_level) + + return original_disable_level + + def set_level(self, level: int | str, logger: str | None = None) -> None: + """Set the threshold level of a logger for the duration of a test. + + Logging messages which are less severe than this level will not be captured. + + .. versionchanged:: 3.4 + The levels of the loggers changed by this function will be + restored to their initial values at the end of the test. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + # Save the original log-level to restore it during teardown. + self._initial_logger_levels.setdefault(logger, logger_obj.level) + logger_obj.setLevel(level) + if self._initial_handler_level is None: + self._initial_handler_level = self.handler.level + self.handler.setLevel(level) + initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) + if self._initial_disabled_logging_level is None: + self._initial_disabled_logging_level = initial_disabled_logging_level + + @contextmanager + def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]: + """Context manager that sets the level for capturing of logs. After + the end of the 'with' statement the level is restored to its original + value. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + orig_level = logger_obj.level + logger_obj.setLevel(level) + handler_orig_level = self.handler.level + self.handler.setLevel(level) + original_disable_level = self._force_enable_logging(level, logger_obj) + try: + yield + finally: + logger_obj.setLevel(orig_level) + self.handler.setLevel(handler_orig_level) + logging.disable(original_disable_level) + + @contextmanager + def filtering(self, filter_: logging.Filter) -> Generator[None]: + """Context manager that temporarily adds the given filter to the caplog's + :meth:`handler` for the 'with' statement block, and removes that filter at the + end of the block. + + :param filter_: A custom :class:`logging.Filter` object. + + .. versionadded:: 7.5 + """ + self.handler.addFilter(filter_) + try: + yield + finally: + self.handler.removeFilter(filter_) + + +@fixture +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]: + """Access and control log capturing. + + Captured logs are available through the following properties/methods:: + + * caplog.messages -> list of format-interpolated log messages + * caplog.text -> string containing formatted log output + * caplog.records -> list of logging.LogRecord instances + * caplog.record_tuples -> list of (logger_name, level, message) tuples + * caplog.clear() -> clear captured records and formatted log output string + """ + result = LogCaptureFixture(request.node, _ispytest=True) + yield result + result._finalize() + + +def get_log_level_for_setting(config: Config, *setting_names: str) -> int | None: + for setting_name in setting_names: + log_level = config.getoption(setting_name) + if log_level is None: + log_level = config.getini(setting_name) + if log_level: + break + else: + return None + + if isinstance(log_level, str): + log_level = log_level.upper() + try: + return int(getattr(logging, log_level, log_level)) + except ValueError as e: + # Python logging does not recognise this as a logging level + raise UsageError( + f"'{log_level}' is not recognized as a logging level name for " + f"'{setting_name}'. Please consider passing the " + "logging level num instead." + ) from e + + +# run after terminalreporter/capturemanager are configured +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") + + +class LoggingPlugin: + """Attaches to the logging module and captures log messages for each test.""" + + def __init__(self, config: Config) -> None: + """Create a new plugin to capture log messages. + + The formatter can be safely shared across all handlers so + create a single one for the entire test session here. + """ + self._config = config + + # Report logging. + self.formatter = self._create_formatter( + get_option_ini(config, "log_format"), + get_option_ini(config, "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_level = get_log_level_for_setting(config, "log_level") + self.caplog_handler = LogCaptureHandler() + self.caplog_handler.setFormatter(self.formatter) + self.report_handler = LogCaptureHandler() + self.report_handler.setFormatter(self.formatter) + + # File logging. + self.log_file_level = get_log_level_for_setting( + config, "log_file_level", "log_level" + ) + log_file = get_option_ini(config, "log_file") or os.devnull + if log_file != os.devnull: + directory = os.path.dirname(os.path.abspath(log_file)) + if not os.path.isdir(directory): + os.makedirs(directory) + + self.log_file_mode = get_option_ini(config, "log_file_mode") or "w" + self.log_file_handler = _FileHandler( + log_file, mode=self.log_file_mode, encoding="UTF-8" + ) + log_file_format = get_option_ini(config, "log_file_format", "log_format") + log_file_date_format = get_option_ini( + config, "log_file_date_format", "log_date_format" + ) + + log_file_formatter = DatetimeFormatter( + log_file_format, datefmt=log_file_date_format + ) + self.log_file_handler.setFormatter(log_file_formatter) + + # CLI/live logging. + self.log_cli_level = get_log_level_for_setting( + config, "log_cli_level", "log_level" + ) + if self._log_cli_enabled(): + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + # Guaranteed by `_log_cli_enabled()`. + assert terminal_reporter is not None + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + self.log_cli_handler: ( + _LiveLoggingStreamHandler | _LiveLoggingNullHandler + ) = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + else: + self.log_cli_handler = _LiveLoggingNullHandler() + log_cli_formatter = self._create_formatter( + get_option_ini(config, "log_cli_format", "log_format"), + get_option_ini(config, "log_cli_date_format", "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_cli_handler.setFormatter(log_cli_formatter) + self._disable_loggers(loggers_to_disable=config.option.logger_disable) + + def _disable_loggers(self, loggers_to_disable: list[str]) -> None: + if not loggers_to_disable: + return + + for name in loggers_to_disable: + logger = logging.getLogger(name) + logger.disabled = True + + def _create_formatter(self, log_format, log_date_format, auto_indent): + # Color option doesn't exist if terminal plugin is disabled. + color = getattr(self._config.option, "color", "no") + if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format + ): + formatter: logging.Formatter = ColoredLevelFormatter( + create_terminal_writer(self._config), log_format, log_date_format + ) + else: + formatter = DatetimeFormatter(log_format, log_date_format) + + formatter._style = PercentStyleMultiline( + formatter._style._fmt, auto_indent=auto_indent + ) + + return formatter + + def set_log_path(self, fname: str) -> None: + """Set the filename parameter for Logging.FileHandler(). + + Creates parent directory if it does not exist. + + .. warning:: + This is an experimental API. + """ + fpath = Path(fname) + + if not fpath.is_absolute(): + fpath = self._config.rootpath / fpath + + if not fpath.parent.exists(): + fpath.parent.mkdir(exist_ok=True, parents=True) + + # https://github.com/python/mypy/issues/11193 + stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding="UTF-8") # type: ignore[assignment] + old_stream = self.log_file_handler.setStream(stream) + if old_stream: + old_stream.close() + + def _log_cli_enabled(self) -> bool: + """Return whether live logging is enabled.""" + enabled = self._config.getoption( + "--log-cli-level" + ) is not None or self._config.getini("log_cli") + if not enabled: + return False + + terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter is None: + # terminal reporter is disabled e.g. by pytest-xdist. + return False + + return True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionstart(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionstart") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection(self) -> Generator[None]: + self.log_cli_handler.set_when("collection") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtestloop(self, session: Session) -> Generator[None, object, object]: + if session.config.option.collectonly: + return (yield) + + if self._log_cli_enabled() and self._config.get_verbosity() < 1: + # The verbose flag is needed to avoid messy test progress output. + self._config.option.verbose = 1 + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) # Run all the tests. + + @hookimpl + def pytest_runtest_logstart(self) -> None: + self.log_cli_handler.reset() + self.log_cli_handler.set_when("start") + + @hookimpl + def pytest_runtest_logreport(self) -> None: + self.log_cli_handler.set_when("logreport") + + @contextmanager + def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]: + """Implement the internals of the pytest_runtest_xxx() hooks.""" + with ( + catching_logs( + self.caplog_handler, + level=self.log_level, + ) as caplog_handler, + catching_logs( + self.report_handler, + level=self.log_level, + ) as report_handler, + ): + caplog_handler.reset() + report_handler.reset() + item.stash[caplog_records_key][when] = caplog_handler.records + item.stash[caplog_handler_key] = caplog_handler + + try: + yield + finally: + log = report_handler.stream.getvalue().strip() + item.add_report_section(when, "log", log) + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("setup") + + empty: dict[str, list[logging.LogRecord]] = {} + item.stash[caplog_records_key] = empty + with self._runtest_for(item, "setup"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("call") + + with self._runtest_for(item, "call"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("teardown") + + try: + with self._runtest_for(item, "teardown"): + yield + finally: + del item.stash[caplog_records_key] + del item.stash[caplog_handler_key] + + @hookimpl + def pytest_runtest_logfinish(self) -> None: + self.log_cli_handler.set_when("finish") + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionfinish(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionfinish") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl + def pytest_unconfigure(self) -> None: + # Close the FileHandler explicitly. + # (logging.shutdown might have lost the weakref?!) + self.log_file_handler.close() + + +class _FileHandler(logging.FileHandler): + """A logging FileHandler with pytest tweaks.""" + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingStreamHandler(logging_StreamHandler): + """A logging StreamHandler used by the live logging feature: it will + write a newline before the first log message in each test. + + During live logging we must also explicitly disable stdout/stderr + capturing otherwise it will get captured and won't appear in the + terminal. + """ + + # Officially stream needs to be a IO[str], but TerminalReporter + # isn't. So force it. + stream: TerminalReporter = None # type: ignore + + def __init__( + self, + terminal_reporter: TerminalReporter, + capture_manager: CaptureManager | None, + ) -> None: + super().__init__(stream=terminal_reporter) # type: ignore[arg-type] + self.capture_manager = capture_manager + self.reset() + self.set_when(None) + self._test_outcome_written = False + + def reset(self) -> None: + """Reset the handler; should be called before the start of each test.""" + self._first_record_emitted = False + + def set_when(self, when: str | None) -> None: + """Prepare for the given test phase (setup/call/teardown).""" + self._when = when + self._section_name_shown = False + if when == "start": + self._test_outcome_written = False + + def emit(self, record: logging.LogRecord) -> None: + ctx_manager = ( + self.capture_manager.global_and_fixture_disabled() + if self.capture_manager + else nullcontext() + ) + with ctx_manager: + if not self._first_record_emitted: + self.stream.write("\n") + self._first_record_emitted = True + elif self._when in ("teardown", "finish"): + if not self._test_outcome_written: + self._test_outcome_written = True + self.stream.write("\n") + if not self._section_name_shown and self._when: + self.stream.section("live log " + self._when, sep="-", bold=True) + self._section_name_shown = True + super().emit(record) + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingNullHandler(logging.NullHandler): + """A logging handler used when live logging is disabled.""" + + def reset(self) -> None: + pass + + def set_when(self, when: str) -> None: + pass + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass diff --git a/.venv/lib/python3.11/site-packages/_pytest/main.py b/.venv/lib/python3.11/site-packages/_pytest/main.py new file mode 100644 index 00000000..dac084b5 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/main.py @@ -0,0 +1,1076 @@ +"""Core implementation of the testing process: init, session, runtest loop.""" + +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import dataclasses +import fnmatch +import functools +import importlib +import importlib.util +import os +from pathlib import Path +import sys +from typing import final +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import warnings + +import pluggy + +from _pytest import nodes +import _pytest._code +from _pytest.config import Config +from _pytest.config import directory_arg +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.config.compat import PathAwareHookProxy +from _pytest.outcomes import exit +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import fnmatch_ex +from _pytest.pathlib import safe_exists +from _pytest.pathlib import scandir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.runner import collect_one_node +from _pytest.runner import SetupState +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + from _pytest.fixtures import FixtureManager + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general", "Running and selection options") + group._addoption( # private to use reserved lower-case short option + "-x", + "--exitfirst", + action="store_const", + dest="maxfail", + const=1, + help="Exit instantly on first error or failed test", + ) + group.addoption( + "--maxfail", + metavar="num", + action="store", + type=int, + dest="maxfail", + default=0, + help="Exit after first num failures or errors", + ) + group.addoption( + "--strict-config", + action="store_true", + help="Any warnings encountered while parsing the `pytest` section of the " + "configuration file raise errors", + ) + group.addoption( + "--strict-markers", + action="store_true", + help="Markers not registered in the `markers` section of the configuration " + "file raise errors", + ) + group.addoption( + "--strict", + action="store_true", + help="(Deprecated) alias to --strict-markers", + ) + + group = parser.getgroup("pytest-warnings") + group.addoption( + "-W", + "--pythonwarnings", + action="append", + help="Set which warnings to report, see -W option of Python itself", + ) + parser.addini( + "filterwarnings", + type="linelist", + help="Each line specifies a pattern for " + "warnings.filterwarnings. " + "Processed after -W/--pythonwarnings.", + ) + + group = parser.getgroup("collect", "collection") + group.addoption( + "--collectonly", + "--collect-only", + "--co", + action="store_true", + help="Only collect tests, don't execute them", + ) + group.addoption( + "--pyargs", + action="store_true", + help="Try to interpret all arguments as Python packages", + ) + group.addoption( + "--ignore", + action="append", + metavar="path", + help="Ignore path during collection (multi-allowed)", + ) + group.addoption( + "--ignore-glob", + action="append", + metavar="path", + help="Ignore path pattern during collection (multi-allowed)", + ) + group.addoption( + "--deselect", + action="append", + metavar="nodeid_prefix", + help="Deselect item (via node id prefix) during collection (multi-allowed)", + ) + group.addoption( + "--confcutdir", + dest="confcutdir", + default=None, + metavar="dir", + type=functools.partial(directory_arg, optname="--confcutdir"), + help="Only load conftest.py's relative to specified dir", + ) + group.addoption( + "--noconftest", + action="store_true", + dest="noconftest", + default=False, + help="Don't load any conftest.py files", + ) + group.addoption( + "--keepduplicates", + "--keep-duplicates", + action="store_true", + dest="keepduplicates", + default=False, + help="Keep duplicate tests", + ) + group.addoption( + "--collect-in-virtualenv", + action="store_true", + dest="collect_in_virtualenv", + default=False, + help="Don't ignore tests in a local virtualenv directory", + ) + group.addoption( + "--continue-on-collection-errors", + action="store_true", + default=False, + dest="continue_on_collection_errors", + help="Force test execution even if collection errors occur", + ) + group.addoption( + "--import-mode", + default="prepend", + choices=["prepend", "append", "importlib"], + dest="importmode", + help="Prepend/append to sys.path when importing test modules and conftest " + "files. Default: prepend.", + ) + parser.addini( + "norecursedirs", + "Directory patterns to avoid for recursion", + type="args", + default=[ + "*.egg", + ".*", + "_darcs", + "build", + "CVS", + "dist", + "node_modules", + "venv", + "{arch}", + ], + ) + parser.addini( + "testpaths", + "Directories to search for tests when no files or directories are given on the " + "command line", + type="args", + default=[], + ) + parser.addini( + "collect_imported_tests", + "Whether to collect tests in imported modules outside `testpaths`", + type="bool", + default=True, + ) + parser.addini( + "consider_namespace_packages", + type="bool", + default=False, + help="Consider namespace packages when resolving module names during import", + ) + + group = parser.getgroup("debugconfig", "test session debugging and configuration") + group._addoption( # private to use reserved lower-case short option + "-c", + "--config-file", + metavar="FILE", + type=str, + dest="inifilename", + help="Load configuration from `FILE` instead of trying to locate one of the " + "implicit configuration files.", + ) + group.addoption( + "--rootdir", + action="store", + dest="rootdir", + help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " + "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " + "'$HOME/root_dir'.", + ) + group.addoption( + "--basetemp", + dest="basetemp", + default=None, + type=validate_basetemp, + metavar="dir", + help=( + "Base temporary directory for this test run. " + "(Warning: this directory is removed if it exists.)" + ), + ) + + +def validate_basetemp(path: str) -> str: + # GH 7119 + msg = "basetemp must not be empty, the current working directory or any parent directory of it" + + # empty path + if not path: + raise argparse.ArgumentTypeError(msg) + + def is_ancestor(base: Path, query: Path) -> bool: + """Return whether query is an ancestor of base.""" + if base == query: + return True + return query in base.parents + + # check if path is an ancestor of cwd + if is_ancestor(Path.cwd(), Path(path).absolute()): + raise argparse.ArgumentTypeError(msg) + + # check symlinks for ancestors + if is_ancestor(Path.cwd().resolve(), Path(path).resolve()): + raise argparse.ArgumentTypeError(msg) + + return path + + +def wrap_session( + config: Config, doit: Callable[[Config, Session], int | ExitCode | None] +) -> int | ExitCode: + """Skeleton command line program.""" + session = Session.from_config(config) + session.exitstatus = ExitCode.OK + initstate = 0 + try: + try: + config._do_configure() + initstate = 1 + config.hook.pytest_sessionstart(session=session) + initstate = 2 + session.exitstatus = doit(config, session) or 0 + except UsageError: + session.exitstatus = ExitCode.USAGE_ERROR + raise + except Failed: + session.exitstatus = ExitCode.TESTS_FAILED + except (KeyboardInterrupt, exit.Exception): + excinfo = _pytest._code.ExceptionInfo.from_current() + exitstatus: int | ExitCode = ExitCode.INTERRUPTED + if isinstance(excinfo.value, exit.Exception): + if excinfo.value.returncode is not None: + exitstatus = excinfo.value.returncode + if initstate < 2: + sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") + config.hook.pytest_keyboard_interrupt(excinfo=excinfo) + session.exitstatus = exitstatus + except BaseException: + session.exitstatus = ExitCode.INTERNAL_ERROR + excinfo = _pytest._code.ExceptionInfo.from_current() + try: + config.notify_exception(excinfo, config.option) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + else: + if isinstance(excinfo.value, SystemExit): + sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + + finally: + # Explicitly break reference cycle. + excinfo = None # type: ignore + os.chdir(session.startpath) + if initstate >= 2: + try: + config.hook.pytest_sessionfinish( + session=session, exitstatus=session.exitstatus + ) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + config._ensure_unconfigure() + return session.exitstatus + + +def pytest_cmdline_main(config: Config) -> int | ExitCode: + return wrap_session(config, _main) + + +def _main(config: Config, session: Session) -> int | ExitCode | None: + """Default command line protocol for initialization, session, + running tests and reporting.""" + config.hook.pytest_collection(session=session) + config.hook.pytest_runtestloop(session=session) + + if session.testsfailed: + return ExitCode.TESTS_FAILED + elif session.testscollected == 0: + return ExitCode.NO_TESTS_COLLECTED + return None + + +def pytest_collection(session: Session) -> None: + session.perform_collect() + + +def pytest_runtestloop(session: Session) -> bool: + if session.testsfailed and not session.config.option.continue_on_collection_errors: + raise session.Interrupted( + f"{session.testsfailed} error{'s' if session.testsfailed != 1 else ''} during collection" + ) + + if session.config.option.collectonly: + return True + + for i, item in enumerate(session.items): + nextitem = session.items[i + 1] if i + 1 < len(session.items) else None + item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) + if session.shouldfail: + raise session.Failed(session.shouldfail) + if session.shouldstop: + raise session.Interrupted(session.shouldstop) + return True + + +def _in_venv(path: Path) -> bool: + """Attempt to detect if ``path`` is the root of a Virtual Environment by + checking for the existence of the pyvenv.cfg file. + + [https://peps.python.org/pep-0405/] + + For regression protection we also check for conda environments that do not include pyenv.cfg yet -- + https://github.com/conda/conda/issues/13337 is the conda issue tracking adding pyenv.cfg. + + Checking for the `conda-meta/history` file per https://github.com/pytest-dev/pytest/issues/12652#issuecomment-2246336902. + + """ + try: + return ( + path.joinpath("pyvenv.cfg").is_file() + or path.joinpath("conda-meta", "history").is_file() + ) + except OSError: + return False + + +def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None: + if collection_path.name == "__pycache__": + return True + + ignore_paths = config._getconftest_pathlist( + "collect_ignore", path=collection_path.parent + ) + ignore_paths = ignore_paths or [] + excludeopt = config.getoption("ignore") + if excludeopt: + ignore_paths.extend(absolutepath(x) for x in excludeopt) + + if collection_path in ignore_paths: + return True + + ignore_globs = config._getconftest_pathlist( + "collect_ignore_glob", path=collection_path.parent + ) + ignore_globs = ignore_globs or [] + excludeglobopt = config.getoption("ignore_glob") + if excludeglobopt: + ignore_globs.extend(absolutepath(x) for x in excludeglobopt) + + if any(fnmatch.fnmatch(str(collection_path), str(glob)) for glob in ignore_globs): + return True + + allow_in_venv = config.getoption("collect_in_virtualenv") + if not allow_in_venv and _in_venv(collection_path): + return True + + if collection_path.is_dir(): + norecursepatterns = config.getini("norecursedirs") + if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns): + return True + + return None + + +def pytest_collect_directory( + path: Path, parent: nodes.Collector +) -> nodes.Collector | None: + return Dir.from_parent(parent, path=path) + + +def pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> None: + deselect_prefixes = tuple(config.getoption("deselect") or []) + if not deselect_prefixes: + return + + remaining = [] + deselected = [] + for colitem in items: + if colitem.nodeid.startswith(deselect_prefixes): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +class FSHookProxy: + def __init__( + self, + pm: PytestPluginManager, + remove_mods: AbstractSet[object], + ) -> None: + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name: str) -> pluggy.HookCaller: + x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) + self.__dict__[name] = x + return x + + +class Interrupted(KeyboardInterrupt): + """Signals that the test run was interrupted.""" + + __module__ = "builtins" # For py3. + + +class Failed(Exception): + """Signals a stop as failed test run.""" + + +@dataclasses.dataclass +class _bestrelpath_cache(dict[Path, str]): + __slots__ = ("path",) + + path: Path + + def __missing__(self, path: Path) -> str: + r = bestrelpath(self.path, path) + self[path] = r + return r + + +@final +class Dir(nodes.Directory): + """Collector of files in a file system directory. + + .. versionadded:: 8.0 + + .. note:: + + Python directories with an `__init__.py` file are instead collected by + :class:`~pytest.Package` by default. Both are :class:`~pytest.Directory` + collectors. + """ + + @classmethod + def from_parent( # type: ignore[override] + cls, + parent: nodes.Collector, + *, + path: Path, + ) -> Self: + """The public constructor. + + :param parent: The parent collector of this Dir. + :param path: The directory's path. + :type path: pathlib.Path + """ + return super().from_parent(parent=parent, path=path) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + config = self.config + col: nodes.Collector | None + cols: Sequence[nodes.Collector] + ihook = self.ihook + for direntry in scandir(self.path): + if direntry.is_dir(): + path = Path(direntry.path) + if not self.session.isinitpath(path, with_parents=True): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + col = ihook.pytest_collect_directory(path=path, parent=self) + if col is not None: + yield col + + elif direntry.is_file(): + path = Path(direntry.path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + cols = ihook.pytest_collect_file(file_path=path, parent=self) + yield from cols + + +@final +class Session(nodes.Collector): + """The root of the collection tree. + + ``Session`` collects the initial paths given as arguments to pytest. + """ + + Interrupted = Interrupted + Failed = Failed + # Set on the session by runner.pytest_sessionstart. + _setupstate: SetupState + # Set on the session by fixtures.pytest_sessionstart. + _fixturemanager: FixtureManager + exitstatus: int | ExitCode + + def __init__(self, config: Config) -> None: + super().__init__( + name="", + path=config.rootpath, + fspath=None, + parent=None, + config=config, + session=self, + nodeid="", + ) + self.testsfailed = 0 + self.testscollected = 0 + self._shouldstop: bool | str = False + self._shouldfail: bool | str = False + self.trace = config.trace.root.get("collection") + self._initialpaths: frozenset[Path] = frozenset() + self._initialpaths_with_parents: frozenset[Path] = frozenset() + self._notfound: list[tuple[str, Sequence[nodes.Collector]]] = [] + self._initial_parts: list[CollectionArgument] = [] + self._collection_cache: dict[nodes.Collector, CollectReport] = {} + self.items: list[nodes.Item] = [] + + self._bestrelpathcache: dict[Path, str] = _bestrelpath_cache(config.rootpath) + + self.config.pluginmanager.register(self, name="session") + + @classmethod + def from_config(cls, config: Config) -> Session: + session: Session = cls._create(config=config) + return session + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.name} " + f"exitstatus=%r " + f"testsfailed={self.testsfailed} " + f"testscollected={self.testscollected}>" + ) % getattr(self, "exitstatus", "") + + @property + def shouldstop(self) -> bool | str: + return self._shouldstop + + @shouldstop.setter + def shouldstop(self, value: bool | str) -> None: + # The runner checks shouldfail and assumes that if it is set we are + # definitely stopping, so prevent unsetting it. + if value is False and self._shouldstop: + warnings.warn( + PytestWarning( + "session.shouldstop cannot be unset after it has been set; ignoring." + ), + stacklevel=2, + ) + return + self._shouldstop = value + + @property + def shouldfail(self) -> bool | str: + return self._shouldfail + + @shouldfail.setter + def shouldfail(self, value: bool | str) -> None: + # The runner checks shouldfail and assumes that if it is set we are + # definitely stopping, so prevent unsetting it. + if value is False and self._shouldfail: + warnings.warn( + PytestWarning( + "session.shouldfail cannot be unset after it has been set; ignoring." + ), + stacklevel=2, + ) + return + self._shouldfail = value + + @property + def startpath(self) -> Path: + """The path from which pytest was invoked. + + .. versionadded:: 7.0.0 + """ + return self.config.invocation_params.dir + + def _node_location_to_relpath(self, node_path: Path) -> str: + # bestrelpath is a quite slow function. + return self._bestrelpathcache[node_path] + + @hookimpl(tryfirst=True) + def pytest_collectstart(self) -> None: + if self.shouldfail: + raise self.Failed(self.shouldfail) + if self.shouldstop: + raise self.Interrupted(self.shouldstop) + + @hookimpl(tryfirst=True) + def pytest_runtest_logreport(self, report: TestReport | CollectReport) -> None: + if report.failed and not hasattr(report, "wasxfail"): + self.testsfailed += 1 + maxfail = self.config.getvalue("maxfail") + if maxfail and self.testsfailed >= maxfail: + self.shouldfail = f"stopping after {self.testsfailed} failures" + + pytest_collectreport = pytest_runtest_logreport + + def isinitpath( + self, + path: str | os.PathLike[str], + *, + with_parents: bool = False, + ) -> bool: + """Is path an initial path? + + An initial path is a path explicitly given to pytest on the command + line. + + :param with_parents: + If set, also return True if the path is a parent of an initial path. + + .. versionchanged:: 8.0 + Added the ``with_parents`` parameter. + """ + # Optimization: Path(Path(...)) is much slower than isinstance. + path_ = path if isinstance(path, Path) else Path(path) + if with_parents: + return path_ in self._initialpaths_with_parents + else: + return path_ in self._initialpaths + + def gethookproxy(self, fspath: os.PathLike[str]) -> pluggy.HookRelay: + # Optimization: Path(Path(...)) is much slower than isinstance. + path = fspath if isinstance(fspath, Path) else Path(fspath) + pm = self.config.pluginmanager + # Check if we have the common case of running + # hooks with all conftest.py files. + my_conftestmodules = pm._getconftestmodules(path) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + proxy: pluggy.HookRelay + if remove_mods: + # One or more conftests are not in use at this path. + proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment] + else: + # All plugins are active for this fspath. + proxy = self.config.hook + return proxy + + def _collect_path( + self, + path: Path, + path_cache: dict[Path, Sequence[nodes.Collector]], + ) -> Sequence[nodes.Collector]: + """Create a Collector for the given path. + + `path_cache` makes it so the same Collectors are returned for the same + path. + """ + if path in path_cache: + return path_cache[path] + + if path.is_dir(): + ihook = self.gethookproxy(path.parent) + col: nodes.Collector | None = ihook.pytest_collect_directory( + path=path, parent=self + ) + cols: Sequence[nodes.Collector] = (col,) if col is not None else () + + elif path.is_file(): + ihook = self.gethookproxy(path) + cols = ihook.pytest_collect_file(file_path=path, parent=self) + + else: + # Broken symlink or invalid/missing file. + cols = () + + path_cache[path] = cols + return cols + + @overload + def perform_collect( + self, args: Sequence[str] | None = ..., genitems: Literal[True] = ... + ) -> Sequence[nodes.Item]: ... + + @overload + def perform_collect( + self, args: Sequence[str] | None = ..., genitems: bool = ... + ) -> Sequence[nodes.Item | nodes.Collector]: ... + + def perform_collect( + self, args: Sequence[str] | None = None, genitems: bool = True + ) -> Sequence[nodes.Item | nodes.Collector]: + """Perform the collection phase for this session. + + This is called by the default :hook:`pytest_collection` hook + implementation; see the documentation of this hook for more details. + For testing purposes, it may also be called directly on a fresh + ``Session``. + + This function normally recursively expands any collectors collected + from the session to their items, and only items are returned. For + testing purposes, this may be suppressed by passing ``genitems=False``, + in which case the return value contains these collectors unexpanded, + and ``session.items`` is empty. + """ + if args is None: + args = self.config.args + + self.trace("perform_collect", self, args) + self.trace.root.indent += 1 + + hook = self.config.hook + + self._notfound = [] + self._initial_parts = [] + self._collection_cache = {} + self.items = [] + items: Sequence[nodes.Item | nodes.Collector] = self.items + try: + initialpaths: list[Path] = [] + initialpaths_with_parents: list[Path] = [] + for arg in args: + collection_argument = resolve_collection_argument( + self.config.invocation_params.dir, + arg, + as_pypath=self.config.option.pyargs, + ) + self._initial_parts.append(collection_argument) + initialpaths.append(collection_argument.path) + initialpaths_with_parents.append(collection_argument.path) + initialpaths_with_parents.extend(collection_argument.path.parents) + self._initialpaths = frozenset(initialpaths) + self._initialpaths_with_parents = frozenset(initialpaths_with_parents) + + rep = collect_one_node(self) + self.ihook.pytest_collectreport(report=rep) + self.trace.root.indent -= 1 + if self._notfound: + errors = [] + for arg, collectors in self._notfound: + if collectors: + errors.append( + f"not found: {arg}\n(no match in any of {collectors!r})" + ) + else: + errors.append(f"found no collectors for {arg}") + + raise UsageError(*errors) + + if not genitems: + items = rep.result + else: + if rep.passed: + for node in rep.result: + self.items.extend(self.genitems(node)) + + self.config.pluginmanager.check_pending() + hook.pytest_collection_modifyitems( + session=self, config=self.config, items=items + ) + finally: + self._notfound = [] + self._initial_parts = [] + self._collection_cache = {} + hook.pytest_collection_finish(session=self) + + if genitems: + self.testscollected = len(items) + + return items + + def _collect_one_node( + self, + node: nodes.Collector, + handle_dupes: bool = True, + ) -> tuple[CollectReport, bool]: + if node in self._collection_cache and handle_dupes: + rep = self._collection_cache[node] + return rep, True + else: + rep = collect_one_node(node) + self._collection_cache[node] = rep + return rep, False + + def collect(self) -> Iterator[nodes.Item | nodes.Collector]: + # This is a cache for the root directories of the initial paths. + # We can't use collection_cache for Session because of its special + # role as the bootstrapping collector. + path_cache: dict[Path, Sequence[nodes.Collector]] = {} + + pm = self.config.pluginmanager + + for collection_argument in self._initial_parts: + self.trace("processing argument", collection_argument) + self.trace.root.indent += 1 + + argpath = collection_argument.path + names = collection_argument.parts + module_name = collection_argument.module_name + + # resolve_collection_argument() ensures this. + if argpath.is_dir(): + assert not names, f"invalid arg {(argpath, names)!r}" + + paths = [argpath] + # Add relevant parents of the path, from the root, e.g. + # /a/b/c.py -> [/, /a, /a/b, /a/b/c.py] + if module_name is None: + # Paths outside of the confcutdir should not be considered. + for path in argpath.parents: + if not pm._is_in_confcutdir(path): + break + paths.insert(0, path) + else: + # For --pyargs arguments, only consider paths matching the module + # name. Paths beyond the package hierarchy are not included. + module_name_parts = module_name.split(".") + for i, path in enumerate(argpath.parents, 2): + if i > len(module_name_parts) or path.stem != module_name_parts[-i]: + break + paths.insert(0, path) + + # Start going over the parts from the root, collecting each level + # and discarding all nodes which don't match the level's part. + any_matched_in_initial_part = False + notfound_collectors = [] + work: list[tuple[nodes.Collector | nodes.Item, list[Path | str]]] = [ + (self, [*paths, *names]) + ] + while work: + matchnode, matchparts = work.pop() + + # Pop'd all of the parts, this is a match. + if not matchparts: + yield matchnode + any_matched_in_initial_part = True + continue + + # Should have been matched by now, discard. + if not isinstance(matchnode, nodes.Collector): + continue + + # Collect this level of matching. + # Collecting Session (self) is done directly to avoid endless + # recursion to this function. + subnodes: Sequence[nodes.Collector | nodes.Item] + if isinstance(matchnode, Session): + assert isinstance(matchparts[0], Path) + subnodes = matchnode._collect_path(matchparts[0], path_cache) + else: + # For backward compat, files given directly multiple + # times on the command line should not be deduplicated. + handle_dupes = not ( + len(matchparts) == 1 + and isinstance(matchparts[0], Path) + and matchparts[0].is_file() + ) + rep, duplicate = self._collect_one_node(matchnode, handle_dupes) + if not duplicate and not rep.passed: + # Report collection failures here to avoid failing to + # run some test specified in the command line because + # the module could not be imported (#134). + matchnode.ihook.pytest_collectreport(report=rep) + if not rep.passed: + continue + subnodes = rep.result + + # Prune this level. + any_matched_in_collector = False + for node in reversed(subnodes): + # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. + if isinstance(matchparts[0], Path): + is_match = node.path == matchparts[0] + if sys.platform == "win32" and not is_match: + # In case the file paths do not match, fallback to samefile() to + # account for short-paths on Windows (#11895). + same_file = os.path.samefile(node.path, matchparts[0]) + # We don't want to match links to the current node, + # otherwise we would match the same file more than once (#12039). + is_match = same_file and ( + os.path.islink(node.path) + == os.path.islink(matchparts[0]) + ) + + # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. + else: + # TODO: Remove parametrized workaround once collection structure contains + # parametrization. + is_match = ( + node.name == matchparts[0] + or node.name.split("[")[0] == matchparts[0] + ) + if is_match: + work.append((node, matchparts[1:])) + any_matched_in_collector = True + + if not any_matched_in_collector: + notfound_collectors.append(matchnode) + + if not any_matched_in_initial_part: + report_arg = "::".join((str(argpath), *names)) + self._notfound.append((report_arg, notfound_collectors)) + + self.trace.root.indent -= 1 + + def genitems(self, node: nodes.Item | nodes.Collector) -> Iterator[nodes.Item]: + self.trace("genitems", node) + if isinstance(node, nodes.Item): + node.ihook.pytest_itemcollected(item=node) + yield node + else: + assert isinstance(node, nodes.Collector) + keepduplicates = self.config.getoption("keepduplicates") + # For backward compat, dedup only applies to files. + handle_dupes = not (keepduplicates and isinstance(node, nodes.File)) + rep, duplicate = self._collect_one_node(node, handle_dupes) + if duplicate and not keepduplicates: + return + if rep.passed: + for subnode in rep.result: + yield from self.genitems(subnode) + if not duplicate: + node.ihook.pytest_collectreport(report=rep) + + +def search_pypath(module_name: str) -> str | None: + """Search sys.path for the given a dotted module name, and return its file + system path if found.""" + try: + spec = importlib.util.find_spec(module_name) + # AttributeError: looks like package module, but actually filename + # ImportError: module does not exist + # ValueError: not a module name + except (AttributeError, ImportError, ValueError): + return None + if spec is None or spec.origin is None or spec.origin == "namespace": + return None + elif spec.submodule_search_locations: + return os.path.dirname(spec.origin) + else: + return spec.origin + + +@dataclasses.dataclass(frozen=True) +class CollectionArgument: + """A resolved collection argument.""" + + path: Path + parts: Sequence[str] + module_name: str | None + + +def resolve_collection_argument( + invocation_path: Path, arg: str, *, as_pypath: bool = False +) -> CollectionArgument: + """Parse path arguments optionally containing selection parts and return (fspath, names). + + Command-line arguments can point to files and/or directories, and optionally contain + parts for specific tests selection, for example: + + "pkg/tests/test_foo.py::TestClass::test_foo" + + This function ensures the path exists, and returns a resolved `CollectionArgument`: + + CollectionArgument( + path=Path("/full/path/to/pkg/tests/test_foo.py"), + parts=["TestClass", "test_foo"], + module_name=None, + ) + + When as_pypath is True, expects that the command-line argument actually contains + module paths instead of file-system paths: + + "pkg.tests.test_foo::TestClass::test_foo" + + In which case we search sys.path for a matching module, and then return the *path* to the + found module, which may look like this: + + CollectionArgument( + path=Path("/home/u/myvenv/lib/site-packages/pkg/tests/test_foo.py"), + parts=["TestClass", "test_foo"], + module_name="pkg.tests.test_foo", + ) + + If the path doesn't exist, raise UsageError. + If the path is a directory and selection parts are present, raise UsageError. + """ + base, squacket, rest = str(arg).partition("[") + strpath, *parts = base.split("::") + if parts: + parts[-1] = f"{parts[-1]}{squacket}{rest}" + module_name = None + if as_pypath: + pyarg_strpath = search_pypath(strpath) + if pyarg_strpath is not None: + module_name = strpath + strpath = pyarg_strpath + fspath = invocation_path / strpath + fspath = absolutepath(fspath) + if not safe_exists(fspath): + msg = ( + "module or package not found: {arg} (missing __init__.py?)" + if as_pypath + else "file or directory not found: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + if parts and fspath.is_dir(): + msg = ( + "package argument cannot contain :: selection parts: {arg}" + if as_pypath + else "directory argument cannot contain :: selection parts: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + return CollectionArgument( + path=fspath, + parts=parts, + module_name=module_name, + ) diff --git a/.venv/lib/python3.11/site-packages/_pytest/mark/__init__.py b/.venv/lib/python3.11/site-packages/_pytest/mark/__init__.py new file mode 100644 index 00000000..068c7410 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/mark/__init__.py @@ -0,0 +1,301 @@ +"""Generic mechanism for marking and selecting python functions.""" + +from __future__ import annotations + +import collections +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Set as AbstractSet +import dataclasses +from typing import Optional +from typing import TYPE_CHECKING + +from .expression import Expression +from .expression import ParseError +from .structures import _HiddenParam +from .structures import EMPTY_PARAMETERSET_OPTION +from .structures import get_empty_parameterset_mark +from .structures import HIDDEN_PARAM +from .structures import Mark +from .structures import MARK_GEN +from .structures import MarkDecorator +from .structures import MarkGenerator +from .structures import ParameterSet +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import NOT_SET +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey + + +if TYPE_CHECKING: + from _pytest.nodes import Item + + +__all__ = [ + "HIDDEN_PARAM", + "MARK_GEN", + "Mark", + "MarkDecorator", + "MarkGenerator", + "ParameterSet", + "get_empty_parameterset_mark", +] + + +old_mark_config_key = StashKey[Optional[Config]]() + + +def param( + *values: object, + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | _HiddenParam | None = None, +) -> ParameterSet: + """Specify a parameter in `pytest.mark.parametrize`_ calls or + :ref:`parametrized fixtures `. + + .. code-block:: python + + @pytest.mark.parametrize( + "test_input,expected", + [ + ("3+5", 8), + pytest.param("6*9", 42, marks=pytest.mark.xfail), + ], + ) + def test_eval(test_input, expected): + assert eval(test_input) == expected + + :param values: Variable args of the values of the parameter set, in order. + + :param marks: + A single mark or a list of marks to be applied to this parameter set. + + :ref:`pytest.mark.usefixtures ` cannot be added via this parameter. + + :type id: str | Literal[pytest.HIDDEN_PARAM] | None + :param id: + The id to attribute to this parameter set. + + .. versionadded:: 8.4 + :ref:`hidden-param` means to hide the parameter set + from the test name. Can only be used at most 1 time, as + test names need to be unique. + """ + return ParameterSet.param(*values, marks=marks, id=id) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( # private to use reserved lower-case short option + "-k", + action="store", + dest="keyword", + default="", + metavar="EXPRESSION", + help="Only run tests which match the given substring expression. " + "An expression is a Python evaluable expression " + "where all names are substring-matched against test names " + "and their parent classes. Example: -k 'test_method or test_" + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other', while -k 'not test_method' " + "matches those that don't contain 'test_method' in their names. " + "-k 'not test_method and not test_other' will eliminate the matches. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' set, " + "as well as functions which have names assigned directly to them. " + "The matching is case-insensitive.", + ) + + group._addoption( # private to use reserved lower-case short option + "-m", + action="store", + dest="markexpr", + default="", + metavar="MARKEXPR", + help="Only run tests matching given mark expression. " + "For example: -m 'mark1 and not mark2'.", + ) + + group.addoption( + "--markers", + action="store_true", + help="show markers (builtin, plugin and per-project ones).", + ) + + parser.addini("markers", "Register new markers for test functions", "linelist") + parser.addini(EMPTY_PARAMETERSET_OPTION, "Default marker for empty parametersets") + + +@hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + import _pytest.config + + if config.option.markers: + config._do_configure() + tw = _pytest.config.create_terminal_writer(config) + for line in config.getini("markers"): + parts = line.split(":", 1) + name = parts[0] + rest = parts[1] if len(parts) == 2 else "" + tw.write(f"@pytest.mark.{name}:", bold=True) + tw.line(rest) + tw.line() + config._ensure_unconfigure() + return 0 + + return None + + +@dataclasses.dataclass +class KeywordMatcher: + """A matcher for keywords. + + Given a list of names, matches any substring of one of these names. The + string inclusion check is case-insensitive. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + + Additionally, matches on names in the 'extra_keyword_matches' set of + any item, as well as names directly assigned to test functions. + """ + + __slots__ = ("_names",) + + _names: AbstractSet[str] + + @classmethod + def from_item(cls, item: Item) -> KeywordMatcher: + mapped_names = set() + + # Add the names of the current item and any parent items, + # except the Session and root Directory's which are not + # interesting for matching. + import pytest + + for node in item.listchain(): + if isinstance(node, pytest.Session): + continue + if isinstance(node, pytest.Directory) and isinstance( + node.parent, pytest.Session + ): + continue + mapped_names.add(node.name) + + # Add the names added as extra keywords to current or parent items. + mapped_names.update(item.listextrakeywords()) + + # Add the names attached to the current function through direct assignment. + function_obj = getattr(item, "function", None) + if function_obj: + mapped_names.update(function_obj.__dict__) + + # Add the markers to the keywords as we no longer handle them correctly. + mapped_names.update(mark.name for mark in item.iter_markers()) + + return cls(mapped_names) + + def __call__(self, subname: str, /, **kwargs: str | int | bool | None) -> bool: + if kwargs: + raise UsageError("Keyword expressions do not support call parameters.") + subname = subname.lower() + return any(subname in name.lower() for name in self._names) + + +def deselect_by_keyword(items: list[Item], config: Config) -> None: + keywordexpr = config.option.keyword.lstrip() + if not keywordexpr: + return + + expr = _parse_expression(keywordexpr, "Wrong expression passed to '-k'") + + remaining = [] + deselected = [] + for colitem in items: + if not expr.evaluate(KeywordMatcher.from_item(colitem)): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +@dataclasses.dataclass +class MarkMatcher: + """A matcher for markers which are present. + + Tries to match on any marker names, attached to the given colitem. + """ + + __slots__ = ("own_mark_name_mapping",) + + own_mark_name_mapping: dict[str, list[Mark]] + + @classmethod + def from_markers(cls, markers: Iterable[Mark]) -> MarkMatcher: + mark_name_mapping = collections.defaultdict(list) + for mark in markers: + mark_name_mapping[mark.name].append(mark) + return cls(mark_name_mapping) + + def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: + if not (matches := self.own_mark_name_mapping.get(name, [])): + return False + + for mark in matches: # pylint: disable=consider-using-any-or-all + if all(mark.kwargs.get(k, NOT_SET) == v for k, v in kwargs.items()): + return True + return False + + +def deselect_by_mark(items: list[Item], config: Config) -> None: + matchexpr = config.option.markexpr + if not matchexpr: + return + + expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'") + remaining: list[Item] = [] + deselected: list[Item] = [] + for item in items: + if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())): + remaining.append(item) + else: + deselected.append(item) + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +def _parse_expression(expr: str, exc_message: str) -> Expression: + try: + return Expression.compile(expr) + except ParseError as e: + raise UsageError(f"{exc_message}: {expr}: {e}") from None + + +def pytest_collection_modifyitems(items: list[Item], config: Config) -> None: + deselect_by_keyword(items, config) + deselect_by_mark(items, config) + + +def pytest_configure(config: Config) -> None: + config.stash[old_mark_config_key] = MARK_GEN._config + MARK_GEN._config = config + + empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) + + if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): + raise UsageError( + f"{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect" + f" but it is {empty_parameterset!r}" + ) + + +def pytest_unconfigure(config: Config) -> None: + MARK_GEN._config = config.stash.get(old_mark_config_key, None) diff --git a/.venv/lib/python3.11/site-packages/_pytest/mark/expression.py b/.venv/lib/python3.11/site-packages/_pytest/mark/expression.py new file mode 100644 index 00000000..743a46bc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/mark/expression.py @@ -0,0 +1,331 @@ +r"""Evaluate match expressions, as used by `-k` and `-m`. + +The grammar is: + +expression: expr? EOF +expr: and_expr ('or' and_expr)* +and_expr: not_expr ('and' not_expr)* +not_expr: 'not' not_expr | '(' expr ')' | ident kwargs? + +ident: (\w|:|\+|-|\.|\[|\]|\\|/)+ +kwargs: ('(' name '=' value ( ', ' name '=' value )* ')') +name: a valid ident, but not a reserved keyword +value: (unescaped) string literal | (-)?[0-9]+ | 'False' | 'True' | 'None' + +The semantics are: + +- Empty expression evaluates to False. +- ident evaluates to True or False according to a provided matcher function. +- or/and/not evaluate according to the usual boolean semantics. +- ident with parentheses and keyword arguments evaluates to True or False according to a provided matcher function. +""" + +from __future__ import annotations + +import ast +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import enum +import keyword +import re +import types +from typing import Literal +from typing import NoReturn +from typing import overload +from typing import Protocol + + +__all__ = [ + "Expression", + "ParseError", +] + + +class TokenType(enum.Enum): + LPAREN = "left parenthesis" + RPAREN = "right parenthesis" + OR = "or" + AND = "and" + NOT = "not" + IDENT = "identifier" + EOF = "end of input" + EQUAL = "=" + STRING = "string literal" + COMMA = "," + + +@dataclasses.dataclass(frozen=True) +class Token: + __slots__ = ("pos", "type", "value") + type: TokenType + value: str + pos: int + + +class ParseError(Exception): + """The expression contains invalid syntax. + + :param column: The column in the line where the error occurred (1-based). + :param message: A description of the error. + """ + + def __init__(self, column: int, message: str) -> None: + self.column = column + self.message = message + + def __str__(self) -> str: + return f"at column {self.column}: {self.message}" + + +class Scanner: + __slots__ = ("current", "tokens") + + def __init__(self, input: str) -> None: + self.tokens = self.lex(input) + self.current = next(self.tokens) + + def lex(self, input: str) -> Iterator[Token]: + pos = 0 + while pos < len(input): + if input[pos] in (" ", "\t"): + pos += 1 + elif input[pos] == "(": + yield Token(TokenType.LPAREN, "(", pos) + pos += 1 + elif input[pos] == ")": + yield Token(TokenType.RPAREN, ")", pos) + pos += 1 + elif input[pos] == "=": + yield Token(TokenType.EQUAL, "=", pos) + pos += 1 + elif input[pos] == ",": + yield Token(TokenType.COMMA, ",", pos) + pos += 1 + elif (quote_char := input[pos]) in ("'", '"'): + end_quote_pos = input.find(quote_char, pos + 1) + if end_quote_pos == -1: + raise ParseError( + pos + 1, + f'closing quote "{quote_char}" is missing', + ) + value = input[pos : end_quote_pos + 1] + if (backslash_pos := input.find("\\")) != -1: + raise ParseError( + backslash_pos + 1, + r'escaping with "\" not supported in marker expression', + ) + yield Token(TokenType.STRING, value, pos) + pos += len(value) + else: + match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\|/)+", input[pos:]) + if match: + value = match.group(0) + if value == "or": + yield Token(TokenType.OR, value, pos) + elif value == "and": + yield Token(TokenType.AND, value, pos) + elif value == "not": + yield Token(TokenType.NOT, value, pos) + else: + yield Token(TokenType.IDENT, value, pos) + pos += len(value) + else: + raise ParseError( + pos + 1, + f'unexpected character "{input[pos]}"', + ) + yield Token(TokenType.EOF, "", pos) + + @overload + def accept(self, type: TokenType, *, reject: Literal[True]) -> Token: ... + + @overload + def accept( + self, type: TokenType, *, reject: Literal[False] = False + ) -> Token | None: ... + + def accept(self, type: TokenType, *, reject: bool = False) -> Token | None: + if self.current.type is type: + token = self.current + if token.type is not TokenType.EOF: + self.current = next(self.tokens) + return token + if reject: + self.reject((type,)) + return None + + def reject(self, expected: Sequence[TokenType]) -> NoReturn: + raise ParseError( + self.current.pos + 1, + "expected {}; got {}".format( + " OR ".join(type.value for type in expected), + self.current.type.value, + ), + ) + + +# True, False and None are legal match expression identifiers, +# but illegal as Python identifiers. To fix this, this prefix +# is added to identifiers in the conversion to Python AST. +IDENT_PREFIX = "$" + + +def expression(s: Scanner) -> ast.Expression: + if s.accept(TokenType.EOF): + ret: ast.expr = ast.Constant(False) + else: + ret = expr(s) + s.accept(TokenType.EOF, reject=True) + return ast.fix_missing_locations(ast.Expression(ret)) + + +def expr(s: Scanner) -> ast.expr: + ret = and_expr(s) + while s.accept(TokenType.OR): + rhs = and_expr(s) + ret = ast.BoolOp(ast.Or(), [ret, rhs]) + return ret + + +def and_expr(s: Scanner) -> ast.expr: + ret = not_expr(s) + while s.accept(TokenType.AND): + rhs = not_expr(s) + ret = ast.BoolOp(ast.And(), [ret, rhs]) + return ret + + +def not_expr(s: Scanner) -> ast.expr: + if s.accept(TokenType.NOT): + return ast.UnaryOp(ast.Not(), not_expr(s)) + if s.accept(TokenType.LPAREN): + ret = expr(s) + s.accept(TokenType.RPAREN, reject=True) + return ret + ident = s.accept(TokenType.IDENT) + if ident: + name = ast.Name(IDENT_PREFIX + ident.value, ast.Load()) + if s.accept(TokenType.LPAREN): + ret = ast.Call(func=name, args=[], keywords=all_kwargs(s)) + s.accept(TokenType.RPAREN, reject=True) + else: + ret = name + return ret + + s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT)) + + +BUILTIN_MATCHERS = {"True": True, "False": False, "None": None} + + +def single_kwarg(s: Scanner) -> ast.keyword: + keyword_name = s.accept(TokenType.IDENT, reject=True) + if not keyword_name.value.isidentifier(): + raise ParseError( + keyword_name.pos + 1, + f"not a valid python identifier {keyword_name.value}", + ) + if keyword.iskeyword(keyword_name.value): + raise ParseError( + keyword_name.pos + 1, + f"unexpected reserved python keyword `{keyword_name.value}`", + ) + s.accept(TokenType.EQUAL, reject=True) + + if value_token := s.accept(TokenType.STRING): + value: str | int | bool | None = value_token.value[1:-1] # strip quotes + else: + value_token = s.accept(TokenType.IDENT, reject=True) + if (number := value_token.value).isdigit() or ( + number.startswith("-") and number[1:].isdigit() + ): + value = int(number) + elif value_token.value in BUILTIN_MATCHERS: + value = BUILTIN_MATCHERS[value_token.value] + else: + raise ParseError( + value_token.pos + 1, + f'unexpected character/s "{value_token.value}"', + ) + + ret = ast.keyword(keyword_name.value, ast.Constant(value)) + return ret + + +def all_kwargs(s: Scanner) -> list[ast.keyword]: + ret = [single_kwarg(s)] + while s.accept(TokenType.COMMA): + ret.append(single_kwarg(s)) + return ret + + +class MatcherCall(Protocol): + def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: ... + + +@dataclasses.dataclass +class MatcherNameAdapter: + matcher: MatcherCall + name: str + + def __bool__(self) -> bool: + return self.matcher(self.name) + + def __call__(self, **kwargs: str | int | bool | None) -> bool: + return self.matcher(self.name, **kwargs) + + +class MatcherAdapter(Mapping[str, MatcherNameAdapter]): + """Adapts a matcher function to a locals mapping as required by eval().""" + + def __init__(self, matcher: MatcherCall) -> None: + self.matcher = matcher + + def __getitem__(self, key: str) -> MatcherNameAdapter: + return MatcherNameAdapter(matcher=self.matcher, name=key[len(IDENT_PREFIX) :]) + + def __iter__(self) -> Iterator[str]: + raise NotImplementedError() + + def __len__(self) -> int: + raise NotImplementedError() + + +class Expression: + """A compiled match expression as used by -k and -m. + + The expression can be evaluated against different matchers. + """ + + __slots__ = ("code",) + + def __init__(self, code: types.CodeType) -> None: + self.code = code + + @classmethod + def compile(cls, input: str) -> Expression: + """Compile a match expression. + + :param input: The input expression - one line. + """ + astexpr = expression(Scanner(input)) + code: types.CodeType = compile( + astexpr, + filename="", + mode="eval", + ) + return Expression(code) + + def evaluate(self, matcher: MatcherCall) -> bool: + """Evaluate the match expression. + + :param matcher: + Given an identifier, should return whether it matches or not. + Should be prepared to handle arbitrary strings as input. + + :returns: Whether the expression matches or not. + """ + ret: bool = bool(eval(self.code, {"__builtins__": {}}, MatcherAdapter(matcher))) + return ret diff --git a/.venv/lib/python3.11/site-packages/_pytest/mark/structures.py b/.venv/lib/python3.11/site-packages/_pytest/mark/structures.py new file mode 100644 index 00000000..f9261076 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/mark/structures.py @@ -0,0 +1,662 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence +import dataclasses +import enum +import inspect +from typing import Any +from typing import final +from typing import NamedTuple +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union +import warnings + +from .._code import getfslineno +from ..compat import NOTSET +from ..compat import NotSetType +from _pytest.config import Config +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import MARKED_FIXTURE +from _pytest.outcomes import fail +from _pytest.raises import AbstractRaises +from _pytest.scope import _ScopeName +from _pytest.warning_types import PytestUnknownMarkWarning + + +if TYPE_CHECKING: + from ..nodes import Node + + +EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" + + +# Singleton type for HIDDEN_PARAM, as described in: +# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions +class _HiddenParam(enum.Enum): + token = 0 + + +#: Can be used as a parameter set id to hide it from the test name. +HIDDEN_PARAM = _HiddenParam.token + + +def istestfunc(func) -> bool: + return callable(func) and getattr(func, "__name__", "") != "" + + +def get_empty_parameterset_mark( + config: Config, argnames: Sequence[str], func +) -> MarkDecorator: + from ..nodes import Collector + + argslisting = ", ".join(argnames) + + fs, lineno = getfslineno(func) + reason = f"got empty parameter set for ({argslisting})" + requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) + if requested_mark in ("", None, "skip"): + mark = MARK_GEN.skip(reason=reason) + elif requested_mark == "xfail": + mark = MARK_GEN.xfail(reason=reason, run=False) + elif requested_mark == "fail_at_collect": + raise Collector.CollectError( + f"Empty parameter set in '{func.__name__}' at line {lineno + 1}" + ) + else: + raise LookupError(requested_mark) + return mark + + +class ParameterSet(NamedTuple): + """A set of values for a set of parameters along with associated marks and + an optional ID for the set. + + Examples:: + + pytest.param(1, 2, 3) + # ParameterSet(values=(1, 2, 3), marks=(), id=None) + + pytest.param("hello", id="greeting") + # ParameterSet(values=("hello",), marks=(), id="greeting") + + # Parameter set with marks + pytest.param(42, marks=pytest.mark.xfail) + # ParameterSet(values=(42,), marks=(MarkDecorator(...),), id=None) + + # From parametrize mark (parameter names + list of parameter sets) + pytest.mark.parametrize( + ("a", "b", "expected"), + [ + (1, 2, 3), + pytest.param(40, 2, 42, id="everything"), + ], + ) + # ParameterSet(values=(1, 2, 3), marks=(), id=None) + # ParameterSet(values=(2, 2, 3), marks=(), id="everything") + """ + + values: Sequence[object | NotSetType] + marks: Collection[MarkDecorator | Mark] + id: str | _HiddenParam | None + + @classmethod + def param( + cls, + *values: object, + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | _HiddenParam | None = None, + ) -> ParameterSet: + if isinstance(marks, MarkDecorator): + marks = (marks,) + else: + assert isinstance(marks, collections.abc.Collection) + if any(i.name == "usefixtures" for i in marks): + raise ValueError( + "pytest.param cannot add pytest.mark.usefixtures; see " + "https://docs.pytest.org/en/stable/reference/reference.html#pytest-param" + ) + + if id is not None: + if not isinstance(id, str) and id is not HIDDEN_PARAM: + raise TypeError( + "Expected id to be a string or a `pytest.HIDDEN_PARAM` sentinel, " + f"got {type(id)}: {id!r}", + ) + return cls(values, marks, id) + + @classmethod + def extract_from( + cls, + parameterset: ParameterSet | Sequence[object] | object, + force_tuple: bool = False, + ) -> ParameterSet: + """Extract from an object or objects. + + :param parameterset: + A legacy style parameterset that may or may not be a tuple, + and may or may not be wrapped into a mess of mark objects. + + :param force_tuple: + Enforce tuple wrapping so single argument tuple values + don't get decomposed and break tests. + """ + if isinstance(parameterset, cls): + return parameterset + if force_tuple: + return cls.param(parameterset) + else: + # TODO: Refactor to fix this type-ignore. Currently the following + # passes type-checking but crashes: + # + # @pytest.mark.parametrize(('x', 'y'), [1, 2]) + # def test_foo(x, y): pass + return cls(parameterset, marks=[], id=None) # type: ignore[arg-type] + + @staticmethod + def _parse_parametrize_args( + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + *args, + **kwargs, + ) -> tuple[Sequence[str], bool]: + if isinstance(argnames, str): + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + force_tuple = len(argnames) == 1 + else: + force_tuple = False + return argnames, force_tuple + + @staticmethod + def _parse_parametrize_parameters( + argvalues: Iterable[ParameterSet | Sequence[object] | object], + force_tuple: bool, + ) -> list[ParameterSet]: + return [ + ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues + ] + + @classmethod + def _for_parametrize( + cls, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + func, + config: Config, + nodeid: str, + ) -> tuple[Sequence[str], list[ParameterSet]]: + argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) + parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) + del argvalues + + if parameters: + # Check all parameter sets have the correct number of values. + for param in parameters: + if len(param.values) != len(argnames): + msg = ( + '{nodeid}: in "parametrize" the number of names ({names_len}):\n' + " {names}\n" + "must be equal to the number of values ({values_len}):\n" + " {values}" + ) + fail( + msg.format( + nodeid=nodeid, + values=param.values, + names=argnames, + names_len=len(argnames), + values_len=len(param.values), + ), + pytrace=False, + ) + else: + # Empty parameter set (likely computed at runtime): create a single + # parameter set with NOTSET values, with the "empty parameter set" mark applied to it. + mark = get_empty_parameterset_mark(config, argnames, func) + parameters.append( + ParameterSet( + values=(NOTSET,) * len(argnames), marks=[mark], id="NOTSET" + ) + ) + return argnames, parameters + + +@final +@dataclasses.dataclass(frozen=True) +class Mark: + """A pytest mark.""" + + #: Name of the mark. + name: str + #: Positional arguments of the mark decorator. + args: tuple[Any, ...] + #: Keyword arguments of the mark decorator. + kwargs: Mapping[str, Any] + + #: Source Mark for ids with parametrize Marks. + _param_ids_from: Mark | None = dataclasses.field(default=None, repr=False) + #: Resolved/generated ids with parametrize Marks. + _param_ids_generated: Sequence[str] | None = dataclasses.field( + default=None, repr=False + ) + + def __init__( + self, + name: str, + args: tuple[Any, ...], + kwargs: Mapping[str, Any], + param_ids_from: Mark | None = None, + param_ids_generated: Sequence[str] | None = None, + *, + _ispytest: bool = False, + ) -> None: + """:meta private:""" + check_ispytest(_ispytest) + # Weirdness to bypass frozen=True. + object.__setattr__(self, "name", name) + object.__setattr__(self, "args", args) + object.__setattr__(self, "kwargs", kwargs) + object.__setattr__(self, "_param_ids_from", param_ids_from) + object.__setattr__(self, "_param_ids_generated", param_ids_generated) + + def _has_param_ids(self) -> bool: + return "ids" in self.kwargs or len(self.args) >= 4 + + def combined_with(self, other: Mark) -> Mark: + """Return a new Mark which is a combination of this + Mark and another Mark. + + Combines by appending args and merging kwargs. + + :param Mark other: The mark to combine with. + :rtype: Mark + """ + assert self.name == other.name + + # Remember source of ids with parametrize Marks. + param_ids_from: Mark | None = None + if self.name == "parametrize": + if other._has_param_ids(): + param_ids_from = other + elif self._has_param_ids(): + param_ids_from = self + + return Mark( + self.name, + self.args + other.args, + dict(self.kwargs, **other.kwargs), + param_ids_from=param_ids_from, + _ispytest=True, + ) + + +# A generic parameter designating an object to which a Mark may +# be applied -- a test function (callable) or class. +# Note: a lambda is not allowed, but this can't be represented. +Markable = TypeVar("Markable", bound=Union[Callable[..., object], type]) + + +@dataclasses.dataclass +class MarkDecorator: + """A decorator for applying a mark on test functions and classes. + + ``MarkDecorators`` are created with ``pytest.mark``:: + + mark1 = pytest.mark.NAME # Simple MarkDecorator + mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator + + and can then be applied as decorators to test functions:: + + @mark2 + def test_function(): + pass + + When a ``MarkDecorator`` is called, it does the following: + + 1. If called with a single class as its only positional argument and no + additional keyword arguments, it attaches the mark to the class so it + gets applied automatically to all test cases found in that class. + + 2. If called with a single function as its only positional argument and + no additional keyword arguments, it attaches the mark to the function, + containing all the arguments already stored internally in the + ``MarkDecorator``. + + 3. When called in any other case, it returns a new ``MarkDecorator`` + instance with the original ``MarkDecorator``'s content updated with + the arguments passed to this call. + + Note: The rules above prevent a ``MarkDecorator`` from storing only a + single function or class reference as its positional argument with no + additional keyword or positional arguments. You can work around this by + using `with_args()`. + """ + + mark: Mark + + def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: + """:meta private:""" + check_ispytest(_ispytest) + self.mark = mark + + @property + def name(self) -> str: + """Alias for mark.name.""" + return self.mark.name + + @property + def args(self) -> tuple[Any, ...]: + """Alias for mark.args.""" + return self.mark.args + + @property + def kwargs(self) -> Mapping[str, Any]: + """Alias for mark.kwargs.""" + return self.mark.kwargs + + @property + def markname(self) -> str: + """:meta private:""" + return self.name # for backward-compat (2.4.1 had this attr) + + def with_args(self, *args: object, **kwargs: object) -> MarkDecorator: + """Return a MarkDecorator with extra arguments added. + + Unlike calling the MarkDecorator, with_args() can be used even + if the sole argument is a callable/class. + """ + mark = Mark(self.name, args, kwargs, _ispytest=True) + return MarkDecorator(self.mark.combined_with(mark), _ispytest=True) + + # Type ignored because the overloads overlap with an incompatible + # return type. Not much we can do about that. Thankfully mypy picks + # the first match so it works out even if we break the rules. + @overload + def __call__(self, arg: Markable) -> Markable: # type: ignore[overload-overlap] + pass + + @overload + def __call__(self, *args: object, **kwargs: object) -> MarkDecorator: + pass + + def __call__(self, *args: object, **kwargs: object): + """Call the MarkDecorator.""" + if args and not kwargs: + func = args[0] + is_class = inspect.isclass(func) + # For staticmethods/classmethods, the marks are eventually fetched from the + # function object, not the descriptor, so unwrap. + unwrapped_func = func + if isinstance(func, (staticmethod, classmethod)): + unwrapped_func = func.__func__ + if len(args) == 1 and (istestfunc(unwrapped_func) or is_class): + store_mark(unwrapped_func, self.mark, stacklevel=3) + return func + return self.with_args(*args, **kwargs) + + +def get_unpacked_marks( + obj: object | type, + *, + consider_mro: bool = True, +) -> list[Mark]: + """Obtain the unpacked marks that are stored on an object. + + If obj is a class and consider_mro is true, return marks applied to + this class and all of its super-classes in MRO order. If consider_mro + is false, only return marks applied directly to this class. + """ + if isinstance(obj, type): + if not consider_mro: + mark_lists = [obj.__dict__.get("pytestmark", [])] + else: + mark_lists = [ + x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__) + ] + mark_list = [] + for item in mark_lists: + if isinstance(item, list): + mark_list.extend(item) + else: + mark_list.append(item) + else: + mark_attribute = getattr(obj, "pytestmark", []) + if isinstance(mark_attribute, list): + mark_list = mark_attribute + else: + mark_list = [mark_attribute] + return list(normalize_mark_list(mark_list)) + + +def normalize_mark_list( + mark_list: Iterable[Mark | MarkDecorator], +) -> Iterable[Mark]: + """ + Normalize an iterable of Mark or MarkDecorator objects into a list of marks + by retrieving the `mark` attribute on MarkDecorator instances. + + :param mark_list: marks to normalize + :returns: A new list of the extracted Mark objects + """ + for mark in mark_list: + mark_obj = getattr(mark, "mark", mark) + if not isinstance(mark_obj, Mark): + raise TypeError(f"got {mark_obj!r} instead of Mark") + yield mark_obj + + +def store_mark(obj, mark: Mark, *, stacklevel: int = 2) -> None: + """Store a Mark on an object. + + This is used to implement the Mark declarations/decorators correctly. + """ + assert isinstance(mark, Mark), mark + + from ..fixtures import getfixturemarker + + if getfixturemarker(obj) is not None: + warnings.warn(MARKED_FIXTURE, stacklevel=stacklevel) + + # Always reassign name to avoid updating pytestmark in a reference that + # was only borrowed. + obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark] + + +# Typing for builtin pytest marks. This is cheating; it gives builtin marks +# special privilege, and breaks modularity. But practicality beats purity... +if TYPE_CHECKING: + + class _SkipMarkDecorator(MarkDecorator): + @overload # type: ignore[override,no-overload-impl] + def __call__(self, arg: Markable) -> Markable: ... + + @overload + def __call__(self, reason: str = ...) -> MarkDecorator: ... + + class _SkipifMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + condition: str | bool = ..., + *conditions: str | bool, + reason: str = ..., + ) -> MarkDecorator: ... + + class _XfailMarkDecorator(MarkDecorator): + @overload # type: ignore[override,no-overload-impl] + def __call__(self, arg: Markable) -> Markable: ... + + @overload + def __call__( + self, + condition: str | bool = False, + *conditions: str | bool, + reason: str = ..., + run: bool = ..., + raises: None + | type[BaseException] + | tuple[type[BaseException], ...] + | AbstractRaises[BaseException] = ..., + strict: bool = ..., + ) -> MarkDecorator: ... + + class _ParametrizeMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + *, + indirect: bool | Sequence[str] = ..., + ids: Iterable[None | str | float | int | bool] + | Callable[[Any], object | None] + | None = ..., + scope: _ScopeName | None = ..., + ) -> MarkDecorator: ... + + class _UsefixturesMarkDecorator(MarkDecorator): + def __call__(self, *fixtures: str) -> MarkDecorator: # type: ignore[override] + ... + + class _FilterwarningsMarkDecorator(MarkDecorator): + def __call__(self, *filters: str) -> MarkDecorator: # type: ignore[override] + ... + + +@final +class MarkGenerator: + """Factory for :class:`MarkDecorator` objects - exposed as + a ``pytest.mark`` singleton instance. + + Example:: + + import pytest + + + @pytest.mark.slowtest + def test_function(): + pass + + applies a 'slowtest' :class:`Mark` on ``test_function``. + """ + + # See TYPE_CHECKING above. + if TYPE_CHECKING: + skip: _SkipMarkDecorator + skipif: _SkipifMarkDecorator + xfail: _XfailMarkDecorator + parametrize: _ParametrizeMarkDecorator + usefixtures: _UsefixturesMarkDecorator + filterwarnings: _FilterwarningsMarkDecorator + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._config: Config | None = None + self._markers: set[str] = set() + + def __getattr__(self, name: str) -> MarkDecorator: + """Generate a new :class:`MarkDecorator` with the given name.""" + if name[0] == "_": + raise AttributeError("Marker name must NOT start with underscore") + + if self._config is not None: + # We store a set of markers as a performance optimisation - if a mark + # name is in the set we definitely know it, but a mark may be known and + # not in the set. We therefore start by updating the set! + if name not in self._markers: + for line in self._config.getini("markers"): + # example lines: "skipif(condition): skip the given test if..." + # or "hypothesis: tests which use Hypothesis", so to get the + # marker name we split on both `:` and `(`. + marker = line.split(":")[0].split("(")[0].strip() + self._markers.add(marker) + + # If the name is not in the set of known marks after updating, + # then it really is time to issue a warning or an error. + if name not in self._markers: + if self._config.option.strict_markers or self._config.option.strict: + fail( + f"{name!r} not found in `markers` configuration option", + pytrace=False, + ) + + # Raise a specific error for common misspellings of "parametrize". + if name in ["parameterize", "parametrise", "parameterise"]: + __tracebackhide__ = True + fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") + + warnings.warn( + f"Unknown pytest.mark.{name} - is this a typo? You can register " + "custom marks to avoid this warning - for details, see " + "https://docs.pytest.org/en/stable/how-to/mark.html", + PytestUnknownMarkWarning, + 2, + ) + + return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True) + + +MARK_GEN = MarkGenerator(_ispytest=True) + + +@final +class NodeKeywords(MutableMapping[str, Any]): + __slots__ = ("_markers", "node", "parent") + + def __init__(self, node: Node) -> None: + self.node = node + self.parent = node.parent + self._markers = {node.name: True} + + def __getitem__(self, key: str) -> Any: + try: + return self._markers[key] + except KeyError: + if self.parent is None: + raise + return self.parent.keywords[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._markers[key] = value + + # Note: we could've avoided explicitly implementing some of the methods + # below and use the collections.abc fallback, but that would be slow. + + def __contains__(self, key: object) -> bool: + return key in self._markers or ( + self.parent is not None and key in self.parent.keywords + ) + + def update( # type: ignore[override] + self, + other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), + **kwds: Any, + ) -> None: + self._markers.update(other) + self._markers.update(kwds) + + def __delitem__(self, key: str) -> None: + raise ValueError("cannot delete key in keywords dict") + + def __iter__(self) -> Iterator[str]: + # Doesn't need to be fast. + yield from self._markers + if self.parent is not None: + for keyword in self.parent.keywords: + # self._marks and self.parent.keywords can have duplicates. + if keyword not in self._markers: + yield keyword + + def __len__(self) -> int: + # Doesn't need to be fast. + return sum(1 for keyword in self) + + def __repr__(self) -> str: + return f"" diff --git a/.venv/lib/python3.11/site-packages/_pytest/monkeypatch.py b/.venv/lib/python3.11/site-packages/_pytest/monkeypatch.py new file mode 100644 index 00000000..1285e571 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/monkeypatch.py @@ -0,0 +1,415 @@ +# mypy: allow-untyped-defs +"""Monkeypatching and mocking functionality.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import MutableMapping +from contextlib import contextmanager +import os +import re +import sys +from typing import Any +from typing import final +from typing import overload +from typing import TypeVar +import warnings + +from _pytest.fixtures import fixture +from _pytest.warning_types import PytestWarning + + +RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") + + +K = TypeVar("K") +V = TypeVar("V") + + +@fixture +def monkeypatch() -> Generator[MonkeyPatch]: + """A convenient fixture for monkey-patching. + + The fixture provides these methods to modify objects, dictionaries, or + :data:`os.environ`: + + * :meth:`monkeypatch.setattr(obj, name, value, raising=True) ` + * :meth:`monkeypatch.delattr(obj, name, raising=True) ` + * :meth:`monkeypatch.setitem(mapping, name, value) ` + * :meth:`monkeypatch.delitem(obj, name, raising=True) ` + * :meth:`monkeypatch.setenv(name, value, prepend=None) ` + * :meth:`monkeypatch.delenv(name, raising=True) ` + * :meth:`monkeypatch.syspath_prepend(path) ` + * :meth:`monkeypatch.chdir(path) ` + * :meth:`monkeypatch.context() ` + + All modifications will be undone after the requesting test function or + fixture has finished. The ``raising`` parameter determines if a :class:`KeyError` + or :class:`AttributeError` will be raised if the set/deletion operation does not have the + specified target. + + To undo modifications done by the fixture in a contained scope, + use :meth:`context() `. + """ + mpatch = MonkeyPatch() + yield mpatch + mpatch.undo() + + +def resolve(name: str) -> object: + # Simplified from zope.dottedname. + parts = name.split(".") + + used = parts.pop(0) + found: object = __import__(used) + for part in parts: + used += "." + part + try: + found = getattr(found, part) + except AttributeError: + pass + else: + continue + # We use explicit un-nesting of the handling block in order + # to avoid nested exceptions. + try: + __import__(used) + except ImportError as ex: + expected = str(ex).split()[-1] + if expected == used: + raise + else: + raise ImportError(f"import error in {used}: {ex}") from ex + found = annotated_getattr(found, part, used) + return found + + +def annotated_getattr(obj: object, name: str, ann: str) -> object: + try: + obj = getattr(obj, name) + except AttributeError as e: + raise AttributeError( + f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}" + ) from e + return obj + + +def derive_importpath(import_path: str, raising: bool) -> tuple[str, object]: + if not isinstance(import_path, str) or "." not in import_path: + raise TypeError(f"must be absolute import path string, not {import_path!r}") + module, attr = import_path.rsplit(".", 1) + target = resolve(module) + if raising: + annotated_getattr(target, attr, ann=module) + return attr, target + + +class Notset: + def __repr__(self) -> str: + return "" + + +notset = Notset() + + +@final +class MonkeyPatch: + """Helper to conveniently monkeypatch attributes/items/environment + variables/syspath. + + Returned by the :fixture:`monkeypatch` fixture. + + .. versionchanged:: 6.2 + Can now also be used directly as `pytest.MonkeyPatch()`, for when + the fixture is not available. In this case, use + :meth:`with MonkeyPatch.context() as mp: ` or remember to call + :meth:`undo` explicitly. + """ + + def __init__(self) -> None: + self._setattr: list[tuple[object, str, object]] = [] + self._setitem: list[tuple[Mapping[Any, Any], object, object]] = [] + self._cwd: str | None = None + self._savesyspath: list[str] | None = None + + @classmethod + @contextmanager + def context(cls) -> Generator[MonkeyPatch]: + """Context manager that returns a new :class:`MonkeyPatch` object + which undoes any patching done inside the ``with`` block upon exit. + + Example: + + .. code-block:: python + + import functools + + + def test_partial(monkeypatch): + with monkeypatch.context() as m: + m.setattr(functools, "partial", 3) + + Useful in situations where it is desired to undo some patches before the test ends, + such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples + of this see :issue:`3290`). + """ + m = cls() + try: + yield m + finally: + m.undo() + + @overload + def setattr( + self, + target: str, + name: object, + value: Notset = ..., + raising: bool = ..., + ) -> None: ... + + @overload + def setattr( + self, + target: object, + name: str, + value: object, + raising: bool = ..., + ) -> None: ... + + def setattr( + self, + target: str | object, + name: object | str, + value: object = notset, + raising: bool = True, + ) -> None: + """ + Set attribute value on target, memorizing the old value. + + For example: + + .. code-block:: python + + import os + + monkeypatch.setattr(os, "getcwd", lambda: "/") + + The code above replaces the :func:`os.getcwd` function by a ``lambda`` which + always returns ``"/"``. + + For convenience, you can specify a string as ``target`` which + will be interpreted as a dotted import path, with the last part + being the attribute name: + + .. code-block:: python + + monkeypatch.setattr("os.getcwd", lambda: "/") + + Raises :class:`AttributeError` if the attribute does not exist, unless + ``raising`` is set to False. + + **Where to patch** + + ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. + There can be many names pointing to any individual object, so for patching to work you must ensure + that you patch the name used by the system under test. + + See the section :ref:`Where to patch ` in the :mod:`unittest.mock` + docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but + applies to ``monkeypatch.setattr`` as well. + """ + __tracebackhide__ = True + import inspect + + if isinstance(value, Notset): + if not isinstance(target, str): + raise TypeError( + "use setattr(target, name, value) or " + "setattr(target, value) with target being a dotted " + "import string" + ) + value = name + name, target = derive_importpath(target, raising) + else: + if not isinstance(name, str): + raise TypeError( + "use setattr(target, name, value) with name being a string or " + "setattr(target, value) with target being a dotted " + "import string" + ) + + oldval = getattr(target, name, notset) + if raising and oldval is notset: + raise AttributeError(f"{target!r} has no attribute {name!r}") + + # avoid class descriptors like staticmethod/classmethod + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + setattr(target, name, value) + + def delattr( + self, + target: object | str, + name: str | Notset = notset, + raising: bool = True, + ) -> None: + """Delete attribute ``name`` from ``target``. + + If no ``name`` is specified and ``target`` is a string + it will be interpreted as a dotted import path with the + last part being the attribute name. + + Raises AttributeError it the attribute does not exist, unless + ``raising`` is set to False. + """ + __tracebackhide__ = True + import inspect + + if isinstance(name, Notset): + if not isinstance(target, str): + raise TypeError( + "use delattr(target, name) or " + "delattr(target) with target being a dotted " + "import string" + ) + name, target = derive_importpath(target, raising) + + if not hasattr(target, name): + if raising: + raise AttributeError(name) + else: + oldval = getattr(target, name, notset) + # Avoid class descriptors like staticmethod/classmethod. + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + delattr(target, name) + + def setitem(self, dic: Mapping[K, V], name: K, value: V) -> None: + """Set dictionary entry ``name`` to value.""" + self._setitem.append((dic, name, dic.get(name, notset))) + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + dic[name] = value # type: ignore[index] + + def delitem(self, dic: Mapping[K, V], name: K, raising: bool = True) -> None: + """Delete ``name`` from dict. + + Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to + False. + """ + if name not in dic: + if raising: + raise KeyError(name) + else: + self._setitem.append((dic, name, dic.get(name, notset))) + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + del dic[name] # type: ignore[attr-defined] + + def setenv(self, name: str, value: str, prepend: str | None = None) -> None: + """Set environment variable ``name`` to ``value``. + + If ``prepend`` is a character, read the current environment variable + value and prepend the ``value`` adjoined with the ``prepend`` + character. + """ + if not isinstance(value, str): + warnings.warn( # type: ignore[unreachable] + PytestWarning( + f"Value of environment variable {name} type should be str, but got " + f"{value!r} (type: {type(value).__name__}); converted to str implicitly" + ), + stacklevel=2, + ) + value = str(value) + if prepend and name in os.environ: + value = value + prepend + os.environ[name] + self.setitem(os.environ, name, value) + + def delenv(self, name: str, raising: bool = True) -> None: + """Delete ``name`` from the environment. + + Raises ``KeyError`` if it does not exist, unless ``raising`` is set to + False. + """ + environ: MutableMapping[str, str] = os.environ + self.delitem(environ, name, raising=raising) + + def syspath_prepend(self, path) -> None: + """Prepend ``path`` to ``sys.path`` list of import locations.""" + if self._savesyspath is None: + self._savesyspath = sys.path[:] + sys.path.insert(0, str(path)) + + # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 + # this is only needed when pkg_resources was already loaded by the namespace package + if "pkg_resources" in sys.modules: + from pkg_resources import fixup_namespace_packages + + fixup_namespace_packages(str(path)) + + # A call to syspathinsert() usually means that the caller wants to + # import some dynamically created files, thus with python3 we + # invalidate its import caches. + # This is especially important when any namespace package is in use, + # since then the mtime based FileFinder cache (that gets created in + # this case already) gets not invalidated when writing the new files + # quickly afterwards. + from importlib import invalidate_caches + + invalidate_caches() + + def chdir(self, path: str | os.PathLike[str]) -> None: + """Change the current working directory to the specified path. + + :param path: + The path to change into. + """ + if self._cwd is None: + self._cwd = os.getcwd() + os.chdir(path) + + def undo(self) -> None: + """Undo previous changes. + + This call consumes the undo stack. Calling it a second time has no + effect unless you do more monkeypatching after the undo call. + + There is generally no need to call `undo()`, since it is + called automatically during tear-down. + + .. note:: + The same `monkeypatch` fixture is used across a + single test function invocation. If `monkeypatch` is used both by + the test function itself and one of the test fixtures, + calling `undo()` will undo all of the changes made in + both functions. + + Prefer to use :meth:`context() ` instead. + """ + for obj, name, value in reversed(self._setattr): + if value is not notset: + setattr(obj, name, value) + else: + delattr(obj, name) + self._setattr[:] = [] + for dictionary, key, value in reversed(self._setitem): + if value is notset: + try: + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + del dictionary[key] # type: ignore[attr-defined] + except KeyError: + pass # Was already deleted, so we have the desired state. + else: + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + dictionary[key] = value # type: ignore[index] + self._setitem[:] = [] + if self._savesyspath is not None: + sys.path[:] = self._savesyspath + self._savesyspath = None + + if self._cwd is not None: + os.chdir(self._cwd) + self._cwd = None diff --git a/.venv/lib/python3.11/site-packages/_pytest/nodes.py b/.venv/lib/python3.11/site-packages/_pytest/nodes.py new file mode 100644 index 00000000..6690f6ab --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/nodes.py @@ -0,0 +1,772 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import abc +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import MutableMapping +from functools import cached_property +from functools import lru_cache +import os +import pathlib +from pathlib import Path +from typing import Any +from typing import cast +from typing import NoReturn +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +import warnings + +import pluggy + +import _pytest._code +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback +from _pytest._code.code import TracebackStyle +from _pytest.compat import LEGACY_PATH +from _pytest.compat import signature +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config.compat import _check_path +from _pytest.deprecated import NODE_CTOR_FSPATH_ARG +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import NodeKeywords +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.stash import Stash +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + # Imported here due to circular import. + from _pytest.main import Session + + +SEP = "/" + +tracebackcutdir = Path(_pytest.__file__).parent + + +_T = TypeVar("_T") + + +def _imply_path( + node_type: type[Node], + path: Path | None, + fspath: LEGACY_PATH | None, +) -> Path: + if fspath is not None: + warnings.warn( + NODE_CTOR_FSPATH_ARG.format( + node_type_name=node_type.__name__, + ), + stacklevel=6, + ) + if path is not None: + if fspath is not None: + _check_path(path, fspath) + return path + else: + assert fspath is not None + return Path(fspath) + + +_NodeType = TypeVar("_NodeType", bound="Node") + + +class NodeMeta(abc.ABCMeta): + """Metaclass used by :class:`Node` to enforce that direct construction raises + :class:`Failed`. + + This behaviour supports the indirection introduced with :meth:`Node.from_parent`, + the named constructor to be used instead of direct construction. The design + decision to enforce indirection with :class:`NodeMeta` was made as a + temporary aid for refactoring the collection tree, which was diagnosed to + have :class:`Node` objects whose creational patterns were overly entangled. + Once the refactoring is complete, this metaclass can be removed. + + See https://github.com/pytest-dev/pytest/projects/3 for an overview of the + progress on detangling the :class:`Node` classes. + """ + + def __call__(cls, *k, **kw) -> NoReturn: + msg = ( + "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" + "See " + "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" + " for more details." + ).format(name=f"{cls.__module__}.{cls.__name__}") + fail(msg, pytrace=False) + + def _create(cls: type[_T], *k, **kw) -> _T: + try: + return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] + except TypeError: + sig = signature(getattr(cls, "__init__")) + known_kw = {k: v for k, v in kw.items() if k in sig.parameters} + from .warning_types import PytestDeprecationWarning + + warnings.warn( + PytestDeprecationWarning( + f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" + "See https://docs.pytest.org/en/stable/deprecations.html" + "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " + "for more details." + ) + ) + + return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc] + + +class Node(abc.ABC, metaclass=NodeMeta): + r"""Base class of :class:`Collector` and :class:`Item`, the components of + the test collection tree. + + ``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the + leaf nodes. + """ + + # Implemented in the legacypath plugin. + #: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage + #: for methods not migrated to ``pathlib.Path`` yet, such as + #: :meth:`Item.reportinfo `. Will be deprecated in + #: a future release, prefer using :attr:`path` instead. + fspath: LEGACY_PATH + + # Use __slots__ to make attribute access faster. + # Note that __dict__ is still available. + __slots__ = ( + "__dict__", + "_nodeid", + "_store", + "config", + "name", + "parent", + "path", + "session", + ) + + def __init__( + self, + name: str, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + nodeid: str | None = None, + ) -> None: + #: A unique name within the scope of the parent node. + self.name: str = name + + #: The parent collector node. + self.parent = parent + + if config: + #: The pytest config object. + self.config: Config = config + else: + if not parent: + raise TypeError("config or parent must be provided") + self.config = parent.config + + if session: + #: The pytest session this node is part of. + self.session: Session = session + else: + if not parent: + raise TypeError("session or parent must be provided") + self.session = parent.session + + if path is None and fspath is None: + path = getattr(parent, "path", None) + #: Filesystem path where this node was collected from (can be None). + self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) + + # The explicit annotation is to avoid publicly exposing NodeKeywords. + #: Keywords/markers collected from all scopes. + self.keywords: MutableMapping[str, Any] = NodeKeywords(self) + + #: The marker objects belonging to this node. + self.own_markers: list[Mark] = [] + + #: Allow adding of extra keywords to use for matching. + self.extra_keyword_matches: set[str] = set() + + if nodeid is not None: + assert "::()" not in nodeid + self._nodeid = nodeid + else: + if not self.parent: + raise TypeError("nodeid or parent must be provided") + self._nodeid = self.parent.nodeid + "::" + self.name + + #: A place where plugins can store information on the node for their + #: own use. + self.stash: Stash = Stash() + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + + @classmethod + def from_parent(cls, parent: Node, **kw) -> Self: + """Public constructor for Nodes. + + This indirection got introduced in order to enable removing + the fragile logic from the node constructors. + + Subclasses can use ``super().from_parent(...)`` when overriding the + construction. + + :param parent: The parent node of this Node. + """ + if "config" in kw: + raise TypeError("config is not a valid argument for from_parent") + if "session" in kw: + raise TypeError("session is not a valid argument for from_parent") + return cls._create(parent=parent, **kw) + + @property + def ihook(self) -> pluggy.HookRelay: + """fspath-sensitive hook proxy used to call pytest hooks.""" + return self.session.gethookproxy(self.path) + + def __repr__(self) -> str: + return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) + + def warn(self, warning: Warning) -> None: + """Issue a warning for this Node. + + Warnings will be displayed after the test session, unless explicitly suppressed. + + :param Warning warning: + The warning instance to issue. + + :raises ValueError: If ``warning`` instance is not a subclass of Warning. + + Example usage: + + .. code-block:: python + + node.warn(PytestWarning("some message")) + node.warn(UserWarning("some message")) + + .. versionchanged:: 6.2 + Any subclass of :class:`Warning` is now accepted, rather than only + :class:`PytestWarning ` subclasses. + """ + # enforce type checks here to avoid getting a generic type error later otherwise. + if not isinstance(warning, Warning): + raise ValueError( + f"warning must be an instance of Warning or subclass, got {warning!r}" + ) + path, lineno = get_fslocation_from_item(self) + assert lineno is not None + warnings.warn_explicit( + warning, + category=None, + filename=str(path), + lineno=lineno + 1, + ) + + # Methods for ordering nodes. + + @property + def nodeid(self) -> str: + """A ::-separated string denoting its collection tree address.""" + return self._nodeid + + def __hash__(self) -> int: + return hash(self._nodeid) + + def setup(self) -> None: + pass + + def teardown(self) -> None: + pass + + def iter_parents(self) -> Iterator[Node]: + """Iterate over all parent collectors starting from and including self + up to the root of the collection tree. + + .. versionadded:: 8.1 + """ + parent: Node | None = self + while parent is not None: + yield parent + parent = parent.parent + + def listchain(self) -> list[Node]: + """Return a list of all parent collectors starting from the root of the + collection tree down to and including self.""" + chain = [] + item: Node | None = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker(self, marker: str | MarkDecorator, append: bool = True) -> None: + """Dynamically add a marker object to the node. + + :param marker: + The marker. + :param append: + Whether to append the marker, or prepend it. + """ + from _pytest.mark import MARK_GEN + + if isinstance(marker, MarkDecorator): + marker_ = marker + elif isinstance(marker, str): + marker_ = getattr(MARK_GEN, marker) + else: + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker_.name] = marker_ + if append: + self.own_markers.append(marker_.mark) + else: + self.own_markers.insert(0, marker_.mark) + + def iter_markers(self, name: str | None = None) -> Iterator[Mark]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of the markers of the node. + """ + return (x[1] for x in self.iter_markers_with_node(name=name)) + + def iter_markers_with_node( + self, name: str | None = None + ) -> Iterator[tuple[Node, Mark]]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of (node, mark) tuples. + """ + for node in self.iter_parents(): + for mark in node.own_markers: + if name is None or getattr(mark, "name", None) == name: + yield node, mark + + @overload + def get_closest_marker(self, name: str) -> Mark | None: ... + + @overload + def get_closest_marker(self, name: str, default: Mark) -> Mark: ... + + def get_closest_marker(self, name: str, default: Mark | None = None) -> Mark | None: + """Return the first marker matching the name, from closest (for + example function) to farther level (for example module level). + + :param default: Fallback return value if no marker was found. + :param name: Name to filter by. + """ + return next(self.iter_markers(name=name), default) + + def listextrakeywords(self) -> set[str]: + """Return a set of all extra keywords in self and any parents.""" + extra_keywords: set[str] = set() + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + + def listnames(self) -> list[str]: + return [x.name for x in self.listchain()] + + def addfinalizer(self, fin: Callable[[], object]) -> None: + """Register a function to be called without arguments when this node is + finalized. + + This method can only be called when this node is active + in a setup chain, for example during self.setup(). + """ + self.session._setupstate.addfinalizer(fin, self) + + def getparent(self, cls: type[_NodeType]) -> _NodeType | None: + """Get the closest parent node (including self) which is an instance of + the given class. + + :param cls: The node class to search for. + :returns: The node, if found. + """ + for node in self.iter_parents(): + if isinstance(node, cls): + return node + return None + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + return excinfo.traceback + + def _repr_failure_py( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> TerminalRepr: + from _pytest.fixtures import FixtureLookupError + + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo.from_exception(excinfo.value.cause) + if isinstance(excinfo.value, fail.Exception): + if not excinfo.value.pytrace: + style = "value" + if isinstance(excinfo.value, FixtureLookupError): + return excinfo.value.formatrepr() + + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] + if self.config.getoption("fulltrace", False): + style = "long" + tbfilter = False + else: + tbfilter = self._traceback_filter + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? + if style is None: + if self.config.getoption("tbstyle", "auto") == "short": + style = "short" + else: + style = "long" + + if self.config.get_verbosity() > 1: + truncate_locals = False + else: + truncate_locals = True + + truncate_args = False if self.config.get_verbosity() > 2 else True + + # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. + # It is possible for a fixture/test to change the CWD while this code runs, which + # would then result in the user seeing confusing paths in the failure message. + # To fix this, if the CWD changed, always display the full absolute path. + # It will be better to just always display paths relative to invocation_dir, but + # this requires a lot of plumbing (#6428). + try: + abspath = Path(os.getcwd()) != self.config.invocation_params.dir + except OSError: + abspath = True + + return excinfo.getrepr( + funcargs=True, + abspath=abspath, + showlocals=self.config.getoption("showlocals", False), + style=style, + tbfilter=tbfilter, + truncate_locals=truncate_locals, + truncate_args=truncate_args, + ) + + def repr_failure( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> str | TerminalRepr: + """Return a representation of a collection or test failure. + + .. seealso:: :ref:`non-python tests` + + :param excinfo: Exception information for the failure. + """ + return self._repr_failure_py(excinfo, style) + + +def get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]: + """Try to extract the actual location from a node, depending on available attributes: + + * "location": a pair (path, lineno) + * "obj": a Python object that the node wraps. + * "path": just a path + + :rtype: A tuple of (str|Path, int) with filename and 0-based line number. + """ + # See Item.location. + location: tuple[str, int | None, str] | None = getattr(node, "location", None) + if location is not None: + return location[:2] + obj = getattr(node, "obj", None) + if obj is not None: + return getfslineno(obj) + return getattr(node, "path", "unknown location"), -1 + + +class Collector(Node, abc.ABC): + """Base class of all collectors. + + Collector create children through `collect()` and thus iteratively build + the collection tree. + """ + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """Collect children (items and collectors) for this collector.""" + raise NotImplementedError("abstract") + + # TODO: This omits the style= parameter which breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException] + ) -> str | TerminalRepr: + """Return a representation of a collection failure. + + :param excinfo: Exception information for the failure. + """ + if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( + "fulltrace", False + ): + exc = excinfo.value + return str(exc.args[0]) + + # Respect explicit tbstyle option, but default to "short" + # (_repr_failure_py uses "long" with "fulltrace" option always). + tbstyle = self.config.getoption("tbstyle", "auto") + if tbstyle == "auto": + tbstyle = "short" + + return self._repr_failure_py(excinfo, style=tbstyle) + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + if hasattr(self, "path"): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.path) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + return ntraceback.filter(excinfo) + return excinfo.traceback + + +@lru_cache(maxsize=1000) +def _check_initialpaths_for_relpath( + initial_paths: frozenset[Path], path: Path +) -> str | None: + if path in initial_paths: + return "" + + for parent in path.parents: + if parent in initial_paths: + return str(path.relative_to(parent)) + + return None + + +class FSCollector(Collector, abc.ABC): + """Base class for filesystem collectors.""" + + def __init__( + self, + fspath: LEGACY_PATH | None = None, + path_or_parent: Path | Node | None = None, + path: Path | None = None, + name: str | None = None, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + ) -> None: + if path_or_parent: + if isinstance(path_or_parent, Node): + assert parent is None + parent = cast(FSCollector, path_or_parent) + elif isinstance(path_or_parent, Path): + assert path is None + path = path_or_parent + + path = _imply_path(type(self), path, fspath=fspath) + if name is None: + name = path.name + if parent is not None and parent.path != path: + try: + rel = path.relative_to(parent.path) + except ValueError: + pass + else: + name = str(rel) + name = name.replace(os.sep, SEP) + self.path = path + + if session is None: + assert parent is not None + session = parent.session + + if nodeid is None: + try: + nodeid = str(self.path.relative_to(session.config.rootpath)) + except ValueError: + nodeid = _check_initialpaths_for_relpath(session._initialpaths, path) + + if nodeid and os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super().__init__( + name=name, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + path=path, + ) + + @classmethod + def from_parent( + cls, + parent, + *, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + **kw, + ) -> Self: + """The public constructor.""" + return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) + + +class File(FSCollector, abc.ABC): + """Base class for collecting tests from a file. + + :ref:`non-python tests`. + """ + + +class Directory(FSCollector, abc.ABC): + """Base class for collecting files from a directory. + + A basic directory collector does the following: goes over the files and + sub-directories in the directory and creates collectors for them by calling + the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, + after checking that they are not ignored using + :hook:`pytest_ignore_collect`. + + The default directory collectors are :class:`~pytest.Dir` and + :class:`~pytest.Package`. + + .. versionadded:: 8.0 + + :ref:`custom directory collectors`. + """ + + +class Item(Node, abc.ABC): + """Base class of all test invocation items. + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None + + def __init__( + self, + name, + parent=None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + **kw, + ) -> None: + # The first two arguments are intentionally passed positionally, + # to keep plugins who define a node type which inherits from + # (pytest.Item, pytest.File) working (see issue #8435). + # They can be made kwargs when the deprecation above is done. + super().__init__( + name, + parent, + config=config, + session=session, + nodeid=nodeid, + **kw, + ) + self._report_sections: list[tuple[str, str, str]] = [] + + #: A list of tuples (name, value) that holds user defined properties + #: for this test. + self.user_properties: list[tuple[str, object]] = [] + + self._check_item_and_collector_diamond_inheritance() + + def _check_item_and_collector_diamond_inheritance(self) -> None: + """ + Check if the current type inherits from both File and Collector + at the same time, emitting a warning accordingly (#8447). + """ + cls = type(self) + + # We inject an attribute in the type to avoid issuing this warning + # for the same class more than once, which is not helpful. + # It is a hack, but was deemed acceptable in order to avoid + # flooding the user in the common case. + attr_name = "_pytest_diamond_inheritance_warning_shown" + if getattr(cls, attr_name, False): + return + setattr(cls, attr_name, True) + + problems = ", ".join( + base.__name__ for base in cls.__bases__ if issubclass(base, Collector) + ) + if problems: + warnings.warn( + f"{cls.__name__} is an Item subclass and should not be a collector, " + f"however its bases {problems} are collectors.\n" + "Please split the Collectors and the Item into separate node types.\n" + "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" + "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", + PytestWarning, + ) + + @abc.abstractmethod + def runtest(self) -> None: + """Run the test case for this item. + + Must be implemented by subclasses. + + .. seealso:: :ref:`non-python tests` + """ + raise NotImplementedError("runtest must be implemented by Item subclass") + + def add_report_section(self, when: str, key: str, content: str) -> None: + """Add a new report section, similar to what's done internally to add + stdout and stderr captured output:: + + item.add_report_section("call", "stdout", "report section contents") + + :param str when: + One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. + :param str key: + Name of the section, can be customized at will. Pytest uses ``"stdout"`` and + ``"stderr"`` internally. + :param str content: + The full contents as a string. + """ + if content: + self._report_sections.append((when, key, content)) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + """Get location information for this item for test reports. + + Returns a tuple with three elements: + + - The path of the test (default ``self.path``) + - The 0-based line number of the test (default ``None``) + - A name of the test to be shown (default ``""``) + + .. seealso:: :ref:`non-python tests` + """ + return self.path, None, "" + + @cached_property + def location(self) -> tuple[str, int | None, str]: + """ + Returns a tuple of ``(relfspath, lineno, testname)`` for this item + where ``relfspath`` is file path relative to ``config.rootpath`` + and lineno is a 0-based line number. + """ + location = self.reportinfo() + path = absolutepath(location[0]) + relfspath = self.session._node_location_to_relpath(path) + assert type(location[2]) is str + return (relfspath, location[1], location[2]) diff --git a/.venv/lib/python3.11/site-packages/_pytest/outcomes.py b/.venv/lib/python3.11/site-packages/_pytest/outcomes.py new file mode 100644 index 00000000..68ba0543 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/outcomes.py @@ -0,0 +1,317 @@ +"""Exception classes and constants handling test outcomes as well as +functions creating them.""" + +from __future__ import annotations + +from collections.abc import Callable +import sys +from typing import Any +from typing import cast +from typing import NoReturn +from typing import Protocol +from typing import TypeVar + +from .warning_types import PytestDeprecationWarning + + +class OutcomeException(BaseException): + """OutcomeException and its subclass instances indicate and contain info + about test and collection outcomes.""" + + def __init__(self, msg: str | None = None, pytrace: bool = True) -> None: + if msg is not None and not isinstance(msg, str): + error_msg = ( # type: ignore[unreachable] + "{} expected string as 'msg' parameter, got '{}' instead.\n" + "Perhaps you meant to use a mark?" + ) + raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__)) + super().__init__(msg) + self.msg = msg + self.pytrace = pytrace + + def __repr__(self) -> str: + if self.msg is not None: + return self.msg + return f"<{self.__class__.__name__} instance>" + + __str__ = __repr__ + + +TEST_OUTCOME = (OutcomeException, Exception) + + +class Skipped(OutcomeException): + # XXX hackish: on 3k we fake to live in the builtins + # in order to have Skipped exception printing shorter/nicer + __module__ = "builtins" + + def __init__( + self, + msg: str | None = None, + pytrace: bool = True, + allow_module_level: bool = False, + *, + _use_item_location: bool = False, + ) -> None: + super().__init__(msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + # If true, the skip location is reported as the item's location, + # instead of the place that raises the exception/calls skip(). + self._use_item_location = _use_item_location + + +class Failed(OutcomeException): + """Raised from an explicit call to pytest.fail().""" + + __module__ = "builtins" + + +class Exit(Exception): + """Raised for immediate program exits (no tracebacks/summaries).""" + + def __init__( + self, msg: str = "unknown reason", returncode: int | None = None + ) -> None: + self.msg = msg + self.returncode = returncode + super().__init__(msg) + + +# We need a callable protocol to add attributes, for discussion see +# https://github.com/python/mypy/issues/2087. + +_F = TypeVar("_F", bound=Callable[..., object]) +_ET = TypeVar("_ET", bound=type[BaseException]) + + +class _WithException(Protocol[_F, _ET]): + Exception: _ET + __call__: _F + + +def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _ET]]: + def decorate(func: _F) -> _WithException[_F, _ET]: + func_with_exception = cast(_WithException[_F, _ET], func) + func_with_exception.Exception = exception_type + return func_with_exception + + return decorate + + +# Exposed helper methods. + + +@_with_exception(Exit) +def exit( + reason: str = "", + returncode: int | None = None, +) -> NoReturn: + """Exit testing process. + + :param reason: + The message to show as the reason for exiting pytest. reason has a default value + only because `msg` is deprecated. + + :param returncode: + Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`. + + :raises pytest.exit.Exception: + The exception that is raised. + """ + __tracebackhide__ = True + raise Exit(reason, returncode) + + +@_with_exception(Skipped) +def skip( + reason: str = "", + *, + allow_module_level: bool = False, +) -> NoReturn: + """Skip an executing test with the given message. + + This function should be called only during testing (setup, call or teardown) or + during collection by using the ``allow_module_level`` flag. This function can + be called in doctests as well. + + :param reason: + The message to show the user as reason for the skip. + + :param allow_module_level: + Allows this function to be called at module level. + Raising the skip exception at module level will stop + the execution of the module and prevent the collection of all tests in the module, + even those defined before the `skip` call. + + Defaults to False. + + :raises pytest.skip.Exception: + The exception that is raised. + + .. note:: + It is better to use the :ref:`pytest.mark.skipif ref` marker when + possible to declare a test to be skipped under certain conditions + like mismatching platforms or dependencies. + Similarly, use the ``# doctest: +SKIP`` directive (see :py:data:`doctest.SKIP`) + to skip a doctest statically. + """ + __tracebackhide__ = True + raise Skipped(msg=reason, allow_module_level=allow_module_level) + + +@_with_exception(Failed) +def fail(reason: str = "", pytrace: bool = True) -> NoReturn: + """Explicitly fail an executing test with the given message. + + :param reason: + The message to show the user as reason for the failure. + + :param pytrace: + If False, msg represents the full failure information and no + python traceback will be reported. + + :raises pytest.fail.Exception: + The exception that is raised. + """ + __tracebackhide__ = True + raise Failed(msg=reason, pytrace=pytrace) + + +class XFailed(Failed): + """Raised from an explicit call to pytest.xfail().""" + + +@_with_exception(XFailed) +def xfail(reason: str = "") -> NoReturn: + """Imperatively xfail an executing test or setup function with the given reason. + + This function should be called only during testing (setup, call or teardown). + + No other code is executed after using ``xfail()`` (it is implemented + internally by raising an exception). + + :param reason: + The message to show the user as reason for the xfail. + + .. note:: + It is better to use the :ref:`pytest.mark.xfail ref` marker when + possible to declare a test to be xfailed under certain conditions + like known bugs or missing features. + + :raises pytest.xfail.Exception: + The exception that is raised. + """ + __tracebackhide__ = True + raise XFailed(reason) + + +def importorskip( + modname: str, + minversion: str | None = None, + reason: str | None = None, + *, + exc_type: type[ImportError] | None = None, +) -> Any: + """Import and return the requested module ``modname``, or skip the + current test if the module cannot be imported. + + :param modname: + The name of the module to import. + :param minversion: + If given, the imported module's ``__version__`` attribute must be at + least this minimal version, otherwise the test is still skipped. + :param reason: + If given, this reason is shown as the message when the module cannot + be imported. + :param exc_type: + The exception that should be captured in order to skip modules. + Must be :py:class:`ImportError` or a subclass. + + If the module can be imported but raises :class:`ImportError`, pytest will + issue a warning to the user, as often users expect the module not to be + found (which would raise :class:`ModuleNotFoundError` instead). + + This warning can be suppressed by passing ``exc_type=ImportError`` explicitly. + + See :ref:`import-or-skip-import-error` for details. + + + :returns: + The imported module. This should be assigned to its canonical name. + + :raises pytest.skip.Exception: + If the module cannot be imported. + + Example:: + + docutils = pytest.importorskip("docutils") + + .. versionadded:: 8.2 + + The ``exc_type`` parameter. + """ + import warnings + + __tracebackhide__ = True + compile(modname, "", "eval") # to catch syntaxerrors + + # Until pytest 9.1, we will warn the user if we catch ImportError (instead of ModuleNotFoundError), + # as this might be hiding an installation/environment problem, which is not usually what is intended + # when using importorskip() (#11523). + # In 9.1, to keep the function signature compatible, we just change the code below to: + # 1. Use `exc_type = ModuleNotFoundError` if `exc_type` is not given. + # 2. Remove `warn_on_import` and the warning handling. + if exc_type is None: + exc_type = ImportError + warn_on_import_error = True + else: + warn_on_import_error = False + + skipped: Skipped | None = None + warning: Warning | None = None + + with warnings.catch_warnings(): + # Make sure to ignore ImportWarnings that might happen because + # of existing directories with the same name we're trying to + # import but without a __init__.py file. + warnings.simplefilter("ignore") + + try: + __import__(modname) + except exc_type as exc: + # Do not raise or issue warnings inside the catch_warnings() block. + if reason is None: + reason = f"could not import {modname!r}: {exc}" + skipped = Skipped(reason, allow_module_level=True) + + if warn_on_import_error and not isinstance(exc, ModuleNotFoundError): + lines = [ + "", + f"Module '{modname}' was found, but when imported by pytest it raised:", + f" {exc!r}", + "In pytest 9.1 this warning will become an error by default.", + "You can fix the underlying problem, or alternatively overwrite this behavior and silence this " + "warning by passing exc_type=ImportError explicitly.", + "See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror", + ] + warning = PytestDeprecationWarning("\n".join(lines)) + + if warning: + warnings.warn(warning, stacklevel=2) + if skipped: + raise skipped + + mod = sys.modules[modname] + if minversion is None: + return mod + verattr = getattr(mod, "__version__", None) + if minversion is not None: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if verattr is None or Version(verattr) < Version(minversion): + raise Skipped( + f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", + allow_module_level=True, + ) + return mod diff --git a/.venv/lib/python3.11/site-packages/_pytest/pastebin.py b/.venv/lib/python3.11/site-packages/_pytest/pastebin.py new file mode 100644 index 00000000..c7b39d96 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/pastebin.py @@ -0,0 +1,117 @@ +# mypy: allow-untyped-defs +"""Submit failure or test session information to a pastebin service.""" + +from __future__ import annotations + +from io import StringIO +import tempfile +from typing import IO + +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter +import pytest + + +pastebinfile_key = StashKey[IO[bytes]]() + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--pastebin", + metavar="mode", + action="store", + dest="pastebin", + default=None, + choices=["failed", "all"], + help="Send failed|all info to bpaste.net pastebin service", + ) + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + if config.option.pastebin == "all": + tr = config.pluginmanager.getplugin("terminalreporter") + # If no terminal reporter plugin is present, nothing we can do here; + # this can happen when this function executes in a worker node + # when using pytest-xdist, for example. + if tr is not None: + # pastebin file will be UTF-8 encoded binary file. + config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") + oldwrite = tr._tw.write + + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + if isinstance(s, str): + s = s.encode("utf-8") + config.stash[pastebinfile_key].write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config: Config) -> None: + if pastebinfile_key in config.stash: + pastebinfile = config.stash[pastebinfile_key] + # Get terminal contents and delete file. + pastebinfile.seek(0) + sessionlog = pastebinfile.read() + pastebinfile.close() + del config.stash[pastebinfile_key] + # Undo our patching in the terminal reporter. + tr = config.pluginmanager.getplugin("terminalreporter") + del tr._tw.__dict__["write"] + # Write summary. + tr.write_sep("=", "Sending information to Paste Service") + pastebinurl = create_new_paste(sessionlog) + tr.write_line(f"pastebin session-log: {pastebinurl}\n") + + +def create_new_paste(contents: str | bytes) -> str: + """Create a new paste using the bpaste.net service. + + :contents: Paste contents string. + :returns: URL to the pasted contents, or an error message. + """ + import re + from urllib.error import HTTPError + from urllib.parse import urlencode + from urllib.request import urlopen + + params = {"code": contents, "lexer": "text", "expiry": "1week"} + url = "https://bpa.st" + try: + response: str = ( + urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + ) + except HTTPError as e: + with e: # HTTPErrors are also http responses that must be closed! + return f"bad response: {e}" + except OSError as e: # eg urllib.error.URLError + return f"bad response: {e}" + m = re.search(r'href="/raw/(\w+)"', response) + if m: + return f"{url}/show/{m.group(1)}" + else: + return "bad response: invalid format ('" + response + "')" + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + if terminalreporter.config.option.pastebin != "failed": + return + if "failed" in terminalreporter.stats: + terminalreporter.write_sep("=", "Sending information to Paste Service") + for rep in terminalreporter.stats["failed"]: + try: + msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc + except AttributeError: + msg = terminalreporter._getfailureheadline(rep) + file = StringIO() + tw = create_terminal_writer(terminalreporter.config, file) + rep.toterminal(tw) + s = file.getvalue() + assert len(s) + pastebinurl = create_new_paste(s) + terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/.venv/lib/python3.11/site-packages/_pytest/pathlib.py b/.venv/lib/python3.11/site-packages/_pytest/pathlib.py new file mode 100644 index 00000000..b69e8540 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/pathlib.py @@ -0,0 +1,1055 @@ +from __future__ import annotations + +import atexit +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +import contextlib +from enum import Enum +from errno import EBADF +from errno import ELOOP +from errno import ENOENT +from errno import ENOTDIR +import fnmatch +from functools import partial +from importlib.machinery import ModuleSpec +from importlib.machinery import PathFinder +import importlib.util +import itertools +import os +from os.path import expanduser +from os.path import expandvars +from os.path import isabs +from os.path import sep +from pathlib import Path +from pathlib import PurePath +from posixpath import sep as posix_sep +import shutil +import sys +import types +from types import ModuleType +from typing import Any +from typing import TypeVar +import uuid +import warnings + +from _pytest.compat import assert_never +from _pytest.outcomes import skip +from _pytest.warning_types import PytestWarning + + +if sys.version_info < (3, 11): + from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader +else: + from importlib.machinery import NamespaceLoader + +LOCK_TIMEOUT = 60 * 60 * 24 * 3 + +_AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) + +# The following function, variables and comments were +# copied from cpython 3.9 Lib/pathlib.py file. + +# EBADF - guard against macOS `stat` throwing EBADF +_IGNORED_ERRORS = (ENOENT, ENOTDIR, EBADF, ELOOP) + +_IGNORED_WINERRORS = ( + 21, # ERROR_NOT_READY - drive exists but is not accessible + 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself +) + + +def _ignore_error(exception: Exception) -> bool: + return ( + getattr(exception, "errno", None) in _IGNORED_ERRORS + or getattr(exception, "winerror", None) in _IGNORED_WINERRORS + ) + + +def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: + return path.joinpath(".lock") + + +def on_rm_rf_error( + func: Callable[..., Any] | None, + path: str, + excinfo: BaseException + | tuple[type[BaseException], BaseException, types.TracebackType | None], + *, + start_path: Path, +) -> bool: + """Handle known read-only errors during rmtree. + + The returned value is used only by our own tests. + """ + if isinstance(excinfo, BaseException): + exc = excinfo + else: + exc = excinfo[1] + + # Another process removed the file in the middle of the "rm_rf" (xdist for example). + # More context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 + if isinstance(exc, FileNotFoundError): + return False + + if not isinstance(exc, PermissionError): + warnings.warn( + PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}") + ) + return False + + if func not in (os.rmdir, os.remove, os.unlink): + if func not in (os.open,): + warnings.warn( + PytestWarning( + f"(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}" + ) + ) + return False + + # Chmod + retry. + import stat + + def chmod_rw(p: str) -> None: + mode = os.stat(p).st_mode + os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) + + # For files, we need to recursively go upwards in the directories to + # ensure they all are also writable. + p = Path(path) + if p.is_file(): + for parent in p.parents: + chmod_rw(str(parent)) + # Stop when we reach the original path passed to rm_rf. + if parent == start_path: + break + chmod_rw(str(path)) + + func(path) + return True + + +def ensure_extended_length_path(path: Path) -> Path: + """Get the extended-length version of a path (Windows). + + On Windows, by default, the maximum length of a path (MAX_PATH) is 260 + characters, and operations on paths longer than that fail. But it is possible + to overcome this by converting the path to "extended-length" form before + performing the operation: + https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + + On Windows, this function returns the extended-length absolute version of path. + On other platforms it returns path unchanged. + """ + if sys.platform.startswith("win32"): + path = path.resolve() + path = Path(get_extended_length_path_str(str(path))) + return path + + +def get_extended_length_path_str(path: str) -> str: + """Convert a path to a Windows extended length path.""" + long_path_prefix = "\\\\?\\" + unc_long_path_prefix = "\\\\?\\UNC\\" + if path.startswith((long_path_prefix, unc_long_path_prefix)): + return path + # UNC + if path.startswith("\\\\"): + return unc_long_path_prefix + path[2:] + return long_path_prefix + path + + +def rm_rf(path: Path) -> None: + """Remove the path contents recursively, even if some elements + are read-only.""" + path = ensure_extended_length_path(path) + onerror = partial(on_rm_rf_error, start_path=path) + if sys.version_info >= (3, 12): + shutil.rmtree(str(path), onexc=onerror) + else: + shutil.rmtree(str(path), onerror=onerror) + + +def find_prefixed(root: Path, prefix: str) -> Iterator[os.DirEntry[str]]: + """Find all elements in root that begin with the prefix, case-insensitive.""" + l_prefix = prefix.lower() + for x in os.scandir(root): + if x.name.lower().startswith(l_prefix): + yield x + + +def extract_suffixes(iter: Iterable[os.DirEntry[str]], prefix: str) -> Iterator[str]: + """Return the parts of the paths following the prefix. + + :param iter: Iterator over path names. + :param prefix: Expected prefix of the path names. + """ + p_len = len(prefix) + for entry in iter: + yield entry.name[p_len:] + + +def find_suffixes(root: Path, prefix: str) -> Iterator[str]: + """Combine find_prefixes and extract_suffixes.""" + return extract_suffixes(find_prefixed(root, prefix), prefix) + + +def parse_num(maybe_num: str) -> int: + """Parse number path suffixes, returns -1 on error.""" + try: + return int(maybe_num) + except ValueError: + return -1 + + +def _force_symlink(root: Path, target: str | PurePath, link_to: str | Path) -> None: + """Helper to create the current symlink. + + It's full of race conditions that are reasonably OK to ignore + for the context of best effort linking to the latest test run. + + The presumption being that in case of much parallelism + the inaccuracy is going to be acceptable. + """ + current_symlink = root.joinpath(target) + try: + current_symlink.unlink() + except OSError: + pass + try: + current_symlink.symlink_to(link_to) + except Exception: + pass + + +def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: + """Create a directory with an increased number as suffix for the given prefix.""" + for i in range(10): + # try up to 10 times to create the folder + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + new_number = max_existing + 1 + new_path = root.joinpath(f"{prefix}{new_number}") + try: + new_path.mkdir(mode=mode) + except Exception: + pass + else: + _force_symlink(root, prefix + "current", new_path) + return new_path + else: + raise OSError( + "could not create numbered dir with prefix " + f"{prefix} in {root} after 10 tries" + ) + + +def create_cleanup_lock(p: Path) -> Path: + """Create a lock to prevent premature folder cleanup.""" + lock_path = get_lock_path(p) + try: + fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) + except FileExistsError as e: + raise OSError(f"cannot create lockfile in {p}") from e + else: + pid = os.getpid() + spid = str(pid).encode() + os.write(fd, spid) + os.close(fd) + if not lock_path.is_file(): + raise OSError("lock path got renamed after successful creation") + return lock_path + + +def register_cleanup_lock_removal( + lock_path: Path, register: Any = atexit.register +) -> Any: + """Register a cleanup function for removing a lock, by default on atexit.""" + pid = os.getpid() + + def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None: + current_pid = os.getpid() + if current_pid != original_pid: + # fork + return + try: + lock_path.unlink() + except OSError: + pass + + return register(cleanup_on_exit) + + +def maybe_delete_a_numbered_dir(path: Path) -> None: + """Remove a numbered directory if its lock can be obtained and it does + not seem to be in use.""" + path = ensure_extended_length_path(path) + lock_path = None + try: + lock_path = create_cleanup_lock(path) + parent = path.parent + + garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") + path.rename(garbage) + rm_rf(garbage) + except OSError: + # known races: + # * other process did a cleanup at the same time + # * deletable folder was found + # * process cwd (Windows) + return + finally: + # If we created the lock, ensure we remove it even if we failed + # to properly remove the numbered dir. + if lock_path is not None: + try: + lock_path.unlink() + except OSError: + pass + + +def ensure_deletable(path: Path, consider_lock_dead_if_created_before: float) -> bool: + """Check if `path` is deletable based on whether the lock file is expired.""" + if path.is_symlink(): + return False + lock = get_lock_path(path) + try: + if not lock.is_file(): + return True + except OSError: + # we might not have access to the lock file at all, in this case assume + # we don't have access to the entire directory (#7491). + return False + try: + lock_time = lock.stat().st_mtime + except Exception: + return False + else: + if lock_time < consider_lock_dead_if_created_before: + # We want to ignore any errors while trying to remove the lock such as: + # - PermissionDenied, like the file permissions have changed since the lock creation; + # - FileNotFoundError, in case another pytest process got here first; + # and any other cause of failure. + with contextlib.suppress(OSError): + lock.unlink() + return True + return False + + +def try_cleanup(path: Path, consider_lock_dead_if_created_before: float) -> None: + """Try to cleanup a folder if we can ensure it's deletable.""" + if ensure_deletable(path, consider_lock_dead_if_created_before): + maybe_delete_a_numbered_dir(path) + + +def cleanup_candidates(root: Path, prefix: str, keep: int) -> Iterator[Path]: + """List candidates for numbered directories to be removed - follows py.path.""" + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + max_delete = max_existing - keep + entries = find_prefixed(root, prefix) + entries, entries2 = itertools.tee(entries) + numbers = map(parse_num, extract_suffixes(entries2, prefix)) + for entry, number in zip(entries, numbers): + if number <= max_delete: + yield Path(entry) + + +def cleanup_dead_symlinks(root: Path) -> None: + for left_dir in root.iterdir(): + if left_dir.is_symlink(): + if not left_dir.resolve().exists(): + left_dir.unlink() + + +def cleanup_numbered_dir( + root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float +) -> None: + """Cleanup for lock driven numbered directories.""" + if not root.exists(): + return + for path in cleanup_candidates(root, prefix, keep): + try_cleanup(path, consider_lock_dead_if_created_before) + for path in root.glob("garbage-*"): + try_cleanup(path, consider_lock_dead_if_created_before) + + cleanup_dead_symlinks(root) + + +def make_numbered_dir_with_cleanup( + root: Path, + prefix: str, + keep: int, + lock_timeout: float, + mode: int, +) -> Path: + """Create a numbered dir with a cleanup lock and remove old ones.""" + e = None + for i in range(10): + try: + p = make_numbered_dir(root, prefix, mode) + # Only lock the current dir when keep is not 0 + if keep != 0: + lock_path = create_cleanup_lock(p) + register_cleanup_lock_removal(lock_path) + except Exception as exc: + e = exc + else: + consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout + # Register a cleanup for program exit + atexit.register( + cleanup_numbered_dir, + root, + prefix, + keep, + consider_lock_dead_if_created_before, + ) + return p + assert e is not None + raise e + + +def resolve_from_str(input: str, rootpath: Path) -> Path: + input = expanduser(input) + input = expandvars(input) + if isabs(input): + return Path(input) + else: + return rootpath.joinpath(input) + + +def fnmatch_ex(pattern: str, path: str | os.PathLike[str]) -> bool: + """A port of FNMatcher from py.path.common which works with PurePath() instances. + + The difference between this algorithm and PurePath.match() is that the + latter matches "**" glob expressions for each part of the path, while + this algorithm uses the whole path instead. + + For example: + "tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" + with this algorithm, but not with PurePath.match(). + + This algorithm was ported to keep backward-compatibility with existing + settings which assume paths match according this logic. + + References: + * https://bugs.python.org/issue29249 + * https://bugs.python.org/issue34731 + """ + path = PurePath(path) + iswin32 = sys.platform.startswith("win") + + if iswin32 and sep not in pattern and posix_sep in pattern: + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posix_sep, sep) + + if sep not in pattern: + name = path.name + else: + name = str(path) + if path.is_absolute() and not os.path.isabs(pattern): + pattern = f"*{os.sep}{pattern}" + return fnmatch.fnmatch(name, pattern) + + +def parts(s: str) -> set[str]: + parts = s.split(sep) + return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))} + + +def symlink_or_skip( + src: os.PathLike[str] | str, + dst: os.PathLike[str] | str, + **kwargs: Any, +) -> None: + """Make a symlink, or skip the test in case symlinks are not supported.""" + try: + os.symlink(src, dst, **kwargs) + except OSError as e: + skip(f"symlinks not supported: {e}") + + +class ImportMode(Enum): + """Possible values for `mode` parameter of `import_path`.""" + + prepend = "prepend" + append = "append" + importlib = "importlib" + + +class ImportPathMismatchError(ImportError): + """Raised on import_path() if there is a mismatch of __file__'s. + + This can happen when `import_path` is called multiple times with different filenames that has + the same basename but reside in packages + (for example "/tests1/test_foo.py" and "/tests2/test_foo.py"). + """ + + +def import_path( + path: str | os.PathLike[str], + *, + mode: str | ImportMode = ImportMode.prepend, + root: Path, + consider_namespace_packages: bool, +) -> ModuleType: + """ + Import and return a module from the given path, which can be a file (a module) or + a directory (a package). + + :param path: + Path to the file to import. + + :param mode: + Controls the underlying import mechanism that will be used: + + * ImportMode.prepend: the directory containing the module (or package, taking + `__init__.py` files into account) will be put at the *start* of `sys.path` before + being imported with `importlib.import_module`. + + * ImportMode.append: same as `prepend`, but the directory will be appended + to the end of `sys.path`, if not already in `sys.path`. + + * ImportMode.importlib: uses more fine control mechanisms provided by `importlib` + to import the module, which avoids having to muck with `sys.path` at all. It effectively + allows having same-named test modules in different places. + + :param root: + Used as an anchor when mode == ImportMode.importlib to obtain + a unique name for the module being imported so it can safely be stored + into ``sys.modules``. + + :param consider_namespace_packages: + If True, consider namespace packages when resolving module names. + + :raises ImportPathMismatchError: + If after importing the given `path` and the module `__file__` + are different. Only raised in `prepend` and `append` modes. + """ + path = Path(path) + mode = ImportMode(mode) + + if not path.exists(): + raise ImportError(path) + + if mode is ImportMode.importlib: + # Try to import this module using the standard import mechanisms, but + # without touching sys.path. + try: + pkg_root, module_name = resolve_pkg_root_and_module_name( + path, consider_namespace_packages=consider_namespace_packages + ) + except CouldNotResolvePathError: + pass + else: + # If the given module name is already in sys.modules, do not import it again. + with contextlib.suppress(KeyError): + return sys.modules[module_name] + + mod = _import_module_using_spec( + module_name, path, pkg_root, insert_modules=False + ) + if mod is not None: + return mod + + # Could not import the module with the current sys.path, so we fall back + # to importing the file as a single module, not being a part of a package. + module_name = module_name_from_path(path, root) + with contextlib.suppress(KeyError): + return sys.modules[module_name] + + mod = _import_module_using_spec( + module_name, path, path.parent, insert_modules=True + ) + if mod is None: + raise ImportError(f"Can't find module {module_name} at location {path}") + return mod + + try: + pkg_root, module_name = resolve_pkg_root_and_module_name( + path, consider_namespace_packages=consider_namespace_packages + ) + except CouldNotResolvePathError: + pkg_root, module_name = path.parent, path.stem + + # Change sys.path permanently: restoring it at the end of this function would cause surprising + # problems because of delayed imports: for example, a conftest.py file imported by this function + # might have local imports, which would fail at runtime if we restored sys.path. + if mode is ImportMode.append: + if str(pkg_root) not in sys.path: + sys.path.append(str(pkg_root)) + elif mode is ImportMode.prepend: + if str(pkg_root) != sys.path[0]: + sys.path.insert(0, str(pkg_root)) + else: + assert_never(mode) + + importlib.import_module(module_name) + + mod = sys.modules[module_name] + if path.name == "__init__.py": + return mod + + ignore = os.environ.get("PY_IGNORE_IMPORTMISMATCH", "") + if ignore != "1": + module_file = mod.__file__ + if module_file is None: + raise ImportPathMismatchError(module_name, module_file, path) + + if module_file.endswith((".pyc", ".pyo")): + module_file = module_file[:-1] + if module_file.endswith(os.sep + "__init__.py"): + module_file = module_file[: -(len(os.sep + "__init__.py"))] + + try: + is_same = _is_same(str(path), module_file) + except FileNotFoundError: + is_same = False + + if not is_same: + raise ImportPathMismatchError(module_name, module_file, path) + + return mod + + +def _import_module_using_spec( + module_name: str, module_path: Path, module_location: Path, *, insert_modules: bool +) -> ModuleType | None: + """ + Tries to import a module by its canonical name, path, and its parent location. + + :param module_name: + The expected module name, will become the key of `sys.modules`. + + :param module_path: + The file path of the module, for example `/foo/bar/test_demo.py`. + If module is a package, pass the path to the `__init__.py` of the package. + If module is a namespace package, pass directory path. + + :param module_location: + The parent location of the module. + If module is a package, pass the directory containing the `__init__.py` file. + + :param insert_modules: + If True, will call `insert_missing_modules` to create empty intermediate modules + with made-up module names (when importing test files not reachable from `sys.path`). + + Example 1 of parent_module_*: + + module_name: "a.b.c.demo" + module_path: Path("a/b/c/demo.py") + module_location: Path("a/b/c/") + if "a.b.c" is package ("a/b/c/__init__.py" exists), then + parent_module_name: "a.b.c" + parent_module_path: Path("a/b/c/__init__.py") + parent_module_location: Path("a/b/c/") + else: + parent_module_name: "a.b.c" + parent_module_path: Path("a/b/c") + parent_module_location: Path("a/b/") + + Example 2 of parent_module_*: + + module_name: "a.b.c" + module_path: Path("a/b/c/__init__.py") + module_location: Path("a/b/c/") + if "a.b" is package ("a/b/__init__.py" exists), then + parent_module_name: "a.b" + parent_module_path: Path("a/b/__init__.py") + parent_module_location: Path("a/b/") + else: + parent_module_name: "a.b" + parent_module_path: Path("a/b/") + parent_module_location: Path("a/") + """ + # Attempt to import the parent module, seems is our responsibility: + # https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1308-L1311 + parent_module_name, _, name = module_name.rpartition(".") + parent_module: ModuleType | None = None + if parent_module_name: + parent_module = sys.modules.get(parent_module_name) + # If the parent_module lacks the `__path__` attribute, AttributeError when finding a submodule's spec, + # requiring re-import according to the path. + need_reimport = not hasattr(parent_module, "__path__") + if parent_module is None or need_reimport: + # Get parent_location based on location, get parent_path based on path. + if module_path.name == "__init__.py": + # If the current module is in a package, + # need to leave the package first and then enter the parent module. + parent_module_path = module_path.parent.parent + else: + parent_module_path = module_path.parent + + if (parent_module_path / "__init__.py").is_file(): + # If the parent module is a package, loading by __init__.py file. + parent_module_path = parent_module_path / "__init__.py" + + parent_module = _import_module_using_spec( + parent_module_name, + parent_module_path, + parent_module_path.parent, + insert_modules=insert_modules, + ) + + # Checking with sys.meta_path first in case one of its hooks can import this module, + # such as our own assertion-rewrite hook. + for meta_importer in sys.meta_path: + module_name_of_meta = getattr(meta_importer.__class__, "__module__", "") + if module_name_of_meta == "_pytest.assertion.rewrite" and module_path.is_file(): + # Import modules in subdirectories by module_path + # to ensure assertion rewrites are not missed (#12659). + find_spec_path = [str(module_location), str(module_path)] + else: + find_spec_path = [str(module_location)] + + spec = meta_importer.find_spec(module_name, find_spec_path) + + if spec_matches_module_path(spec, module_path): + break + else: + loader = None + if module_path.is_dir(): + # The `spec_from_file_location` matches a loader based on the file extension by default. + # For a namespace package, need to manually specify a loader. + loader = NamespaceLoader(name, module_path, PathFinder()) # type: ignore[arg-type] + + spec = importlib.util.spec_from_file_location( + module_name, str(module_path), loader=loader + ) + + if spec_matches_module_path(spec, module_path): + assert spec is not None + # Find spec and import this module. + mod = importlib.util.module_from_spec(spec) + sys.modules[module_name] = mod + spec.loader.exec_module(mod) # type: ignore[union-attr] + + # Set this module as an attribute of the parent module (#12194). + if parent_module is not None: + setattr(parent_module, name, mod) + + if insert_modules: + insert_missing_modules(sys.modules, module_name) + return mod + + return None + + +def spec_matches_module_path(module_spec: ModuleSpec | None, module_path: Path) -> bool: + """Return true if the given ModuleSpec can be used to import the given module path.""" + if module_spec is None: + return False + + if module_spec.origin: + return Path(module_spec.origin) == module_path + + # Compare the path with the `module_spec.submodule_Search_Locations` in case + # the module is part of a namespace package. + # https://docs.python.org/3/library/importlib.html#importlib.machinery.ModuleSpec.submodule_search_locations + if module_spec.submodule_search_locations: # can be None. + for path in module_spec.submodule_search_locations: + if Path(path) == module_path: + return True + + return False + + +# Implement a special _is_same function on Windows which returns True if the two filenames +# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). +if sys.platform.startswith("win"): + + def _is_same(f1: str, f2: str) -> bool: + return Path(f1) == Path(f2) or os.path.samefile(f1, f2) + +else: + + def _is_same(f1: str, f2: str) -> bool: + return os.path.samefile(f1, f2) + + +def module_name_from_path(path: Path, root: Path) -> str: + """ + Return a dotted module name based on the given path, anchored on root. + + For example: path="projects/src/tests/test_foo.py" and root="/projects", the + resulting module name will be "src.tests.test_foo". + """ + path = path.with_suffix("") + try: + relative_path = path.relative_to(root) + except ValueError: + # If we can't get a relative path to root, use the full path, except + # for the first part ("d:\\" or "/" depending on the platform, for example). + path_parts = path.parts[1:] + else: + # Use the parts for the relative path to the root path. + path_parts = relative_path.parts + + # Module name for packages do not contain the __init__ file, unless + # the `__init__.py` file is at the root. + if len(path_parts) >= 2 and path_parts[-1] == "__init__": + path_parts = path_parts[:-1] + + # Module names cannot contain ".", normalize them to "_". This prevents + # a directory having a "." in the name (".env.310" for example) causing extra intermediate modules. + # Also, important to replace "." at the start of paths, as those are considered relative imports. + path_parts = tuple(x.replace(".", "_") for x in path_parts) + + return ".".join(path_parts) + + +def insert_missing_modules(modules: dict[str, ModuleType], module_name: str) -> None: + """ + Used by ``import_path`` to create intermediate modules when using mode=importlib. + + When we want to import a module as "src.tests.test_foo" for example, we need + to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo", + otherwise "src.tests.test_foo" is not importable by ``__import__``. + """ + module_parts = module_name.split(".") + while module_name: + parent_module_name, _, child_name = module_name.rpartition(".") + if parent_module_name: + parent_module = modules.get(parent_module_name) + if parent_module is None: + try: + # If sys.meta_path is empty, calling import_module will issue + # a warning and raise ModuleNotFoundError. To avoid the + # warning, we check sys.meta_path explicitly and raise the error + # ourselves to fall back to creating a dummy module. + if not sys.meta_path: + raise ModuleNotFoundError + parent_module = importlib.import_module(parent_module_name) + except ModuleNotFoundError: + parent_module = ModuleType( + module_name, + doc="Empty module created by pytest's importmode=importlib.", + ) + modules[parent_module_name] = parent_module + + # Add child attribute to the parent that can reference the child + # modules. + if not hasattr(parent_module, child_name): + setattr(parent_module, child_name, modules[module_name]) + + module_parts.pop(-1) + module_name = ".".join(module_parts) + + +def resolve_package_path(path: Path) -> Path | None: + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + + Returns None if it cannot be determined. + """ + result = None + for parent in itertools.chain((path,), path.parents): + if parent.is_dir(): + if not (parent / "__init__.py").is_file(): + break + if not parent.name.isidentifier(): + break + result = parent + return result + + +def resolve_pkg_root_and_module_name( + path: Path, *, consider_namespace_packages: bool = False +) -> tuple[Path, str]: + """ + Return the path to the directory of the root package that contains the + given Python file, and its module name: + + src/ + app/ + __init__.py + core/ + __init__.py + models.py + + Passing the full path to `models.py` will yield Path("src") and "app.core.models". + + If consider_namespace_packages is True, then we additionally check upwards in the hierarchy + for namespace packages: + + https://packaging.python.org/en/latest/guides/packaging-namespace-packages + + Raises CouldNotResolvePathError if the given path does not belong to a package (missing any __init__.py files). + """ + pkg_root: Path | None = None + pkg_path = resolve_package_path(path) + if pkg_path is not None: + pkg_root = pkg_path.parent + if consider_namespace_packages: + start = pkg_root if pkg_root is not None else path.parent + for candidate in (start, *start.parents): + module_name = compute_module_name(candidate, path) + if module_name and is_importable(module_name, path): + # Point the pkg_root to the root of the namespace package. + pkg_root = candidate + break + + if pkg_root is not None: + module_name = compute_module_name(pkg_root, path) + if module_name: + return pkg_root, module_name + + raise CouldNotResolvePathError(f"Could not resolve for {path}") + + +def is_importable(module_name: str, module_path: Path) -> bool: + """ + Return if the given module path could be imported normally by Python, akin to the user + entering the REPL and importing the corresponding module name directly, and corresponds + to the module_path specified. + + :param module_name: + Full module name that we want to check if is importable. + For example, "app.models". + + :param module_path: + Full path to the python module/package we want to check if is importable. + For example, "/projects/src/app/models.py". + """ + try: + # Note this is different from what we do in ``_import_module_using_spec``, where we explicitly search through + # sys.meta_path to be able to pass the path of the module that we want to import (``meta_importer.find_spec``). + # Using importlib.util.find_spec() is different, it gives the same results as trying to import + # the module normally in the REPL. + spec = importlib.util.find_spec(module_name) + except (ImportError, ValueError, ImportWarning): + return False + else: + return spec_matches_module_path(spec, module_path) + + +def compute_module_name(root: Path, module_path: Path) -> str | None: + """Compute a module name based on a path and a root anchor.""" + try: + path_without_suffix = module_path.with_suffix("") + except ValueError: + # Empty paths (such as Path.cwd()) might break meta_path hooks (like our own assertion rewriter). + return None + + try: + relative = path_without_suffix.relative_to(root) + except ValueError: # pragma: no cover + return None + names = list(relative.parts) + if not names: + return None + if names[-1] == "__init__": + names.pop() + return ".".join(names) + + +class CouldNotResolvePathError(Exception): + """Custom exception raised by resolve_pkg_root_and_module_name.""" + + +def scandir( + path: str | os.PathLike[str], + sort_key: Callable[[os.DirEntry[str]], object] = lambda entry: entry.name, +) -> list[os.DirEntry[str]]: + """Scan a directory recursively, in breadth-first order. + + The returned entries are sorted according to the given key. + The default is to sort by name. + If the directory does not exist, return an empty list. + """ + entries = [] + # Attempt to create a scandir iterator for the given path. + try: + scandir_iter = os.scandir(path) + except FileNotFoundError: + # If the directory does not exist, return an empty list. + return [] + # Use the scandir iterator in a context manager to ensure it is properly closed. + with scandir_iter as s: + for entry in s: + try: + entry.is_file() + except OSError as err: + if _ignore_error(err): + continue + # Reraise non-ignorable errors to avoid hiding issues. + raise + entries.append(entry) + entries.sort(key=sort_key) # type: ignore[arg-type] + return entries + + +def visit( + path: str | os.PathLike[str], recurse: Callable[[os.DirEntry[str]], bool] +) -> Iterator[os.DirEntry[str]]: + """Walk a directory recursively, in breadth-first order. + + The `recurse` predicate determines whether a directory is recursed. + + Entries at each directory level are sorted. + """ + entries = scandir(path) + yield from entries + for entry in entries: + if entry.is_dir() and recurse(entry): + yield from visit(entry.path, recurse) + + +def absolutepath(path: str | os.PathLike[str]) -> Path: + """Convert a path to an absolute path using os.path.abspath. + + Prefer this over Path.resolve() (see #6523). + Prefer this over Path.absolute() (not public, doesn't normalize). + """ + return Path(os.path.abspath(path)) + + +def commonpath(path1: Path, path2: Path) -> Path | None: + """Return the common part shared with the other path, or None if there is + no common part. + + If one path is relative and one is absolute, returns None. + """ + try: + return Path(os.path.commonpath((str(path1), str(path2)))) + except ValueError: + return None + + +def bestrelpath(directory: Path, dest: Path) -> str: + """Return a string which is a relative path from directory to dest such + that directory/bestrelpath == dest. + + The paths must be either both absolute or both relative. + + If no such path can be determined, returns dest. + """ + assert isinstance(directory, Path) + assert isinstance(dest, Path) + if dest == directory: + return os.curdir + # Find the longest common directory. + base = commonpath(directory, dest) + # Can be the case on Windows for two absolute paths on different drives. + # Can be the case for two relative paths without common prefix. + # Can be the case for a relative path and an absolute path. + if not base: + return str(dest) + reldirectory = directory.relative_to(base) + reldest = dest.relative_to(base) + return os.path.join( + # Back from directory to base. + *([os.pardir] * len(reldirectory.parts)), + # Forward from base to dest. + *reldest.parts, + ) + + +def safe_exists(p: Path) -> bool: + """Like Path.exists(), but account for input arguments that might be too long (#11394).""" + try: + return p.exists() + except (ValueError, OSError): + # ValueError: stat: path too long for Windows + # OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect + return False diff --git a/.venv/lib/python3.11/site-packages/_pytest/py.typed b/.venv/lib/python3.11/site-packages/_pytest/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/_pytest/pytester.py b/.venv/lib/python3.11/site-packages/_pytest/pytester.py new file mode 100644 index 00000000..59d2b0be --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/pytester.py @@ -0,0 +1,1775 @@ +# mypy: allow-untyped-defs +"""(Disabled by default) support for testing pytest and pytest plugins. + +PYTEST_DONT_REWRITE +""" + +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence +import contextlib +from fnmatch import fnmatch +import gc +import importlib +from io import StringIO +import locale +import os +from pathlib import Path +import platform +import re +import shutil +import subprocess +import sys +import traceback +from typing import Any +from typing import Final +from typing import final +from typing import IO +from typing import Literal +from typing import overload +from typing import TextIO +from typing import TYPE_CHECKING +from weakref import WeakKeyDictionary + +from iniconfig import IniConfig +from iniconfig import SectionWrapper + +from _pytest import timing +from _pytest._code import Source +from _pytest.capture import _get_multicapture +from _pytest.compat import NOTSET +from _pytest.compat import NotSetType +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import main +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import importorskip +from _pytest.outcomes import skip +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import make_numbered_dir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.tmpdir import TempPathFactory +from _pytest.warning_types import PytestFDWarning + + +if TYPE_CHECKING: + import pexpect + + +pytest_plugins = ["pytester_assertions"] + + +IGNORE_PAM = [ # filenames added when obtaining details about the current user + "/var/lib/sss/mc/passwd" +] + + +def pytest_addoption(parser: Parser) -> None: + parser.addoption( + "--lsof", + action="store_true", + dest="lsof", + default=False, + help="Run FD checks if lsof is available", + ) + + parser.addoption( + "--runpytest", + default="inprocess", + dest="runpytest", + choices=("inprocess", "subprocess"), + help=( + "Run pytest sub runs in tests using an 'inprocess' " + "or 'subprocess' (python -m main) method" + ), + ) + + parser.addini( + "pytester_example_dir", help="Directory to take the pytester example files from" + ) + + +def pytest_configure(config: Config) -> None: + if config.getvalue("lsof"): + checker = LsofFdLeakChecker() + if checker.matching_platform(): + config.pluginmanager.register(checker) + + config.addinivalue_line( + "markers", + "pytester_example_path(*path_segments): join the given path " + "segments to `pytester_example_dir` for this test.", + ) + + +class LsofFdLeakChecker: + def get_open_files(self) -> list[tuple[str, str]]: + if sys.version_info >= (3, 11): + # New in Python 3.11, ignores utf-8 mode + encoding = locale.getencoding() + else: + encoding = locale.getpreferredencoding(False) + out = subprocess.run( + ("lsof", "-Ffn0", "-p", str(os.getpid())), + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=True, + text=True, + encoding=encoding, + ).stdout + + def isopen(line: str) -> bool: + return line.startswith("f") and ( + "deleted" not in line + and "mem" not in line + and "txt" not in line + and "cwd" not in line + ) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split("\0") + fd = fields[0][1:] + filename = fields[1][1:] + if filename in IGNORE_PAM: + continue + if filename.startswith("/"): + open_files.append((fd, filename)) + + return open_files + + def matching_platform(self) -> bool: + try: + subprocess.run(("lsof", "-v"), check=True) + except (OSError, subprocess.CalledProcessError): + return False + else: + return True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, object, object]: + lines1 = self.get_open_files() + try: + return (yield) + finally: + if hasattr(sys, "pypy_version_info"): + gc.collect() + lines2 = self.get_open_files() + + new_fds = {t[0] for t in lines2} - {t[0] for t in lines1} + leaked_files = [t for t in lines2 if t[0] in new_fds] + if leaked_files: + error = [ + f"***** {len(leaked_files)} FD leakage detected", + *(str(f) for f in leaked_files), + "*** Before:", + *(str(f) for f in lines1), + "*** After:", + *(str(f) for f in lines2), + f"***** {len(leaked_files)} FD leakage detected", + "*** function {}:{}: {} ".format(*item.location), + "See issue #2366", + ] + item.warn(PytestFDWarning("\n".join(error))) + + +# used at least by pytest-xdist plugin + + +@fixture +def _pytest(request: FixtureRequest) -> PytestArg: + """Return a helper which offers a gethookrecorder(hook) method which + returns a HookRecorder instance which helps to make assertions about called + hooks.""" + return PytestArg(request) + + +class PytestArg: + def __init__(self, request: FixtureRequest) -> None: + self._request = request + + def gethookrecorder(self, hook) -> HookRecorder: + hookrecorder = HookRecorder(hook._pm) + self._request.addfinalizer(hookrecorder.finish_recording) + return hookrecorder + + +def get_public_names(values: Iterable[str]) -> list[str]: + """Only return names from iterator values without a leading underscore.""" + return [x for x in values if x[0] != "_"] + + +@final +class RecordedHookCall: + """A recorded call to a hook. + + The arguments to the hook call are set as attributes. + For example: + + .. code-block:: python + + calls = hook_recorder.getcalls("pytest_runtest_setup") + # Suppose pytest_runtest_setup was called once with `item=an_item`. + assert calls[0].item is an_item + """ + + def __init__(self, name: str, kwargs) -> None: + self.__dict__.update(kwargs) + self._name = name + + def __repr__(self) -> str: + d = self.__dict__.copy() + del d["_name"] + return f"" + + if TYPE_CHECKING: + # The class has undetermined attributes, this tells mypy about it. + def __getattr__(self, key: str): ... + + +@final +class HookRecorder: + """Record all hooks called in a plugin manager. + + Hook recorders are created by :class:`Pytester`. + + This wraps all the hook calls in the plugin manager, recording each call + before propagating the normal calls. + """ + + def __init__( + self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + + self._pluginmanager = pluginmanager + self.calls: list[RecordedHookCall] = [] + self.ret: int | ExitCode | None = None + + def before(hook_name: str, hook_impls, kwargs) -> None: + self.calls.append(RecordedHookCall(hook_name, kwargs)) + + def after(outcome, hook_name: str, hook_impls, kwargs) -> None: + pass + + self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) + + def finish_recording(self) -> None: + self._undo_wrapping() + + def getcalls(self, names: str | Iterable[str]) -> list[RecordedHookCall]: + """Get all recorded calls to hooks with the given names (or name).""" + if isinstance(names, str): + names = names.split() + return [call for call in self.calls if call._name in names] + + def assert_contains(self, entries: Sequence[tuple[str, str]]) -> None: + __tracebackhide__ = True + i = 0 + entries = list(entries) + # Since Python 3.13, f_locals is not a dict, but eval requires a dict. + backlocals = dict(sys._getframe(1).f_locals) + while entries: + name, check = entries.pop(0) + for ind, call in enumerate(self.calls[i:]): + if call._name == name: + print("NAMEMATCH", name, call) + if eval(check, backlocals, call.__dict__): + print("CHECKERMATCH", repr(check), "->", call) + else: + print("NOCHECKERMATCH", repr(check), "-", call) + continue + i += ind + 1 + break + print("NONAMEMATCH", name, "with", call) + else: + fail(f"could not find {name!r} check {check!r}") + + def popcall(self, name: str) -> RecordedHookCall: + __tracebackhide__ = True + for i, call in enumerate(self.calls): + if call._name == name: + del self.calls[i] + return call + lines = [f"could not find call {name!r}, in:"] + lines.extend([f" {x}" for x in self.calls]) + fail("\n".join(lines)) + + def getcall(self, name: str) -> RecordedHookCall: + values = self.getcalls(name) + assert len(values) == 1, (name, values) + return values[0] + + # functionality for test reports + + @overload + def getreports( + self, + names: Literal["pytest_collectreport"], + ) -> Sequence[CollectReport]: ... + + @overload + def getreports( + self, + names: Literal["pytest_runtest_logreport"], + ) -> Sequence[TestReport]: ... + + @overload + def getreports( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: ... + + def getreports( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: + return [x.report for x in self.getcalls(names)] + + def matchreport( + self, + inamepart: str = "", + names: str | Iterable[str] = ( + "pytest_runtest_logreport", + "pytest_collectreport", + ), + when: str | None = None, + ) -> CollectReport | TestReport: + """Return a testreport whose dotted import path matches.""" + values = [] + for rep in self.getreports(names=names): + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those + continue + if when and rep.when != when: + continue + if not inamepart or inamepart in rep.nodeid.split("::"): + values.append(rep) + if not values: + raise ValueError( + f"could not find test report matching {inamepart!r}: " + "no test reports at all!" + ) + if len(values) > 1: + raise ValueError( + f"found 2 or more testreports matching {inamepart!r}: {values}" + ) + return values[0] + + @overload + def getfailures( + self, + names: Literal["pytest_collectreport"], + ) -> Sequence[CollectReport]: ... + + @overload + def getfailures( + self, + names: Literal["pytest_runtest_logreport"], + ) -> Sequence[TestReport]: ... + + @overload + def getfailures( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: ... + + def getfailures( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: + return [rep for rep in self.getreports(names) if rep.failed] + + def getfailedcollections(self) -> Sequence[CollectReport]: + return self.getfailures("pytest_collectreport") + + def listoutcomes( + self, + ) -> tuple[ + Sequence[TestReport], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], + ]: + passed = [] + skipped = [] + failed = [] + for rep in self.getreports( + ("pytest_collectreport", "pytest_runtest_logreport") + ): + if rep.passed: + if rep.when == "call": + assert isinstance(rep, TestReport) + passed.append(rep) + elif rep.skipped: + skipped.append(rep) + else: + assert rep.failed, f"Unexpected outcome: {rep!r}" + failed.append(rep) + return passed, skipped, failed + + def countoutcomes(self) -> list[int]: + return [len(x) for x in self.listoutcomes()] + + def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: + __tracebackhide__ = True + from _pytest.pytester_assertions import assertoutcome + + outcomes = self.listoutcomes() + assertoutcome( + outcomes, + passed=passed, + skipped=skipped, + failed=failed, + ) + + def clear(self) -> None: + self.calls[:] = [] + + +@fixture +def linecomp() -> LineComp: + """A :class: `LineComp` instance for checking that an input linearly + contains a sequence of strings.""" + return LineComp() + + +@fixture(name="LineMatcher") +def LineMatcher_fixture(request: FixtureRequest) -> type[LineMatcher]: + """A reference to the :class: `LineMatcher`. + + This is instantiable with a list of lines (without their trailing newlines). + This is useful for testing large texts, such as the output of commands. + """ + return LineMatcher + + +@fixture +def pytester( + request: FixtureRequest, tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch +) -> Pytester: + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to ``path`` and environment variables during initialization. + + It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path` + fixture but provides methods which aid in testing pytest itself. + """ + return Pytester(request, tmp_path_factory, monkeypatch, _ispytest=True) + + +@fixture +def _sys_snapshot() -> Generator[None]: + snappaths = SysPathsSnapshot() + snapmods = SysModulesSnapshot() + yield + snapmods.restore() + snappaths.restore() + + +@fixture +def _config_for_test() -> Generator[Config]: + from _pytest.config import get_config + + config = get_config() + yield config + config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. + + +# Regex to match the session duration string in the summary: "74.34s". +rex_session_duration = re.compile(r"\d+\.\d\ds") +# Regex to match all the counts and phrases in the summary line: "34 passed, 111 skipped". +rex_outcome = re.compile(r"(\d+) (\w+)") + + +@final +class RunResult: + """The result of running a command from :class:`~pytest.Pytester`.""" + + def __init__( + self, + ret: int | ExitCode, + outlines: list[str], + errlines: list[str], + duration: float, + ) -> None: + try: + self.ret: int | ExitCode = ExitCode(ret) + """The return value.""" + except ValueError: + self.ret = ret + self.outlines = outlines + """List of lines captured from stdout.""" + self.errlines = errlines + """List of lines captured from stderr.""" + self.stdout = LineMatcher(outlines) + """:class:`~pytest.LineMatcher` of stdout. + + Use e.g. :func:`str(stdout) ` to reconstruct stdout, or the commonly used + :func:`stdout.fnmatch_lines() ` method. + """ + self.stderr = LineMatcher(errlines) + """:class:`~pytest.LineMatcher` of stderr.""" + self.duration = duration + """Duration in seconds.""" + + def __repr__(self) -> str: + return ( + f"" + ) + + def parseoutcomes(self) -> dict[str, int]: + """Return a dictionary of outcome noun -> count from parsing the terminal + output that the test process produced. + + The returned nouns will always be in plural form:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + return self.parse_summary_nouns(self.outlines) + + @classmethod + def parse_summary_nouns(cls, lines) -> dict[str, int]: + """Extract the nouns from a pytest terminal summary line. + + It always returns the plural noun for consistency:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + for line in reversed(lines): + if rex_session_duration.search(line): + outcomes = rex_outcome.findall(line) + ret = {noun: int(count) for (count, noun) in outcomes} + break + else: + raise ValueError("Pytest terminal summary report not found") + + to_plural = { + "warning": "warnings", + "error": "errors", + } + return {to_plural.get(k, k): v for k, v in ret.items()} + + def assert_outcomes( + self, + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + warnings: int | None = None, + deselected: int | None = None, + ) -> None: + """ + Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run. + + ``warnings`` and ``deselected`` are only checked if not None. + """ + __tracebackhide__ = True + from _pytest.pytester_assertions import assert_outcomes + + outcomes = self.parseoutcomes() + assert_outcomes( + outcomes, + passed=passed, + skipped=skipped, + failed=failed, + errors=errors, + xpassed=xpassed, + xfailed=xfailed, + warnings=warnings, + deselected=deselected, + ) + + +class SysModulesSnapshot: + def __init__(self, preserve: Callable[[str], bool] | None = None) -> None: + self.__preserve = preserve + self.__saved = dict(sys.modules) + + def restore(self) -> None: + if self.__preserve: + self.__saved.update( + (k, m) for k, m in sys.modules.items() if self.__preserve(k) + ) + sys.modules.clear() + sys.modules.update(self.__saved) + + +class SysPathsSnapshot: + def __init__(self) -> None: + self.__saved = list(sys.path), list(sys.meta_path) + + def restore(self) -> None: + sys.path[:], sys.meta_path[:] = self.__saved + + +@final +class Pytester: + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to :attr:`path` and environment variables during initialization. + """ + + __test__ = False + + CLOSE_STDIN: Final = NOTSET + + class TimeoutExpired(Exception): + pass + + def __init__( + self, + request: FixtureRequest, + tmp_path_factory: TempPathFactory, + monkeypatch: MonkeyPatch, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._request = request + self._mod_collections: WeakKeyDictionary[Collector, list[Item | Collector]] = ( + WeakKeyDictionary() + ) + if request.function: + name: str = request.function.__name__ + else: + name = request.node.name + self._name = name + self._path: Path = tmp_path_factory.mktemp(name, numbered=True) + #: A list of plugins to use with :py:meth:`parseconfig` and + #: :py:meth:`runpytest`. Initially this is an empty list but plugins can + #: be added to the list. The type of items to add to the list depends on + #: the method using them so refer to them for details. + self.plugins: list[str | _PluggyPlugin] = [] + self._sys_path_snapshot = SysPathsSnapshot() + self._sys_modules_snapshot = self.__take_sys_modules_snapshot() + self._request.addfinalizer(self._finalize) + self._method = self._request.config.getoption("--runpytest") + self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) + + self._monkeypatch = mp = monkeypatch + self.chdir() + mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) + # Ensure no unexpected caching via tox. + mp.delenv("TOX_ENV_DIR", raising=False) + # Discard outer pytest options. + mp.delenv("PYTEST_ADDOPTS", raising=False) + # Ensure no user config is used. + tmphome = str(self.path) + mp.setenv("HOME", tmphome) + mp.setenv("USERPROFILE", tmphome) + # Do not use colors for inner runs by default. + mp.setenv("PY_COLORS", "0") + + @property + def path(self) -> Path: + """Temporary directory path used to create files/run tests from, etc.""" + return self._path + + def __repr__(self) -> str: + return f"" + + def _finalize(self) -> None: + """ + Clean up global state artifacts. + + Some methods modify the global interpreter state and this tries to + clean this up. It does not remove the temporary directory however so + it can be looked at after the test run has finished. + """ + self._sys_modules_snapshot.restore() + self._sys_path_snapshot.restore() + + def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: + # Some zope modules used by twisted-related tests keep internal state + # and can't be deleted; we had some trouble in the past with + # `zope.interface` for example. + # + # Preserve readline due to https://bugs.python.org/issue41033. + # pexpect issues a SIGWINCH. + def preserve_module(name): + return name.startswith(("zope", "readline")) + + return SysModulesSnapshot(preserve=preserve_module) + + def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: + """Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`.""" + pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined] + self._request.addfinalizer(reprec.finish_recording) + return reprec + + def chdir(self) -> None: + """Cd into the temporary directory. + + This is done automatically upon instantiation. + """ + self._monkeypatch.chdir(self.path) + + def _makefile( + self, + ext: str, + lines: Sequence[Any | bytes], + files: dict[str, str], + encoding: str = "utf-8", + ) -> Path: + items = list(files.items()) + + if ext is None: + raise TypeError("ext must not be None") + + if ext and not ext.startswith("."): + raise ValueError( + f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" + ) + + def to_text(s: Any | bytes) -> str: + return s.decode(encoding) if isinstance(s, bytes) else str(s) + + if lines: + source = "\n".join(to_text(x) for x in lines) + basename = self._name + items.insert(0, (basename, source)) + + ret = None + for basename, value in items: + p = self.path.joinpath(basename).with_suffix(ext) + p.parent.mkdir(parents=True, exist_ok=True) + source_ = Source(value) + source = "\n".join(to_text(line) for line in source_.lines) + p.write_text(source.strip(), encoding=encoding) + if ret is None: + ret = p + assert ret is not None + return ret + + def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: + r"""Create new text file(s) in the test directory. + + :param ext: + The extension the file(s) should use, including the dot, e.g. `.py`. + :param args: + All args are treated as strings and joined using newlines. + The result is written as contents to the file. The name of the + file is based on the test function requesting this fixture. + :param kwargs: + Each keyword is the name of a file, while the value of it will + be written as contents of the file. + :returns: + The first created file. + + Examples: + + .. code-block:: python + + pytester.makefile(".txt", "line1", "line2") + + pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") + + To create binary files, use :meth:`pathlib.Path.write_bytes` directly: + + .. code-block:: python + + filename = pytester.path.joinpath("foo.bin") + filename.write_bytes(b"...") + """ + return self._makefile(ext, args, kwargs) + + def makeconftest(self, source: str) -> Path: + """Write a conftest.py file. + + :param source: The contents. + :returns: The conftest.py file. + """ + return self.makepyfile(conftest=source) + + def makeini(self, source: str) -> Path: + """Write a tox.ini file. + + :param source: The contents. + :returns: The tox.ini file. + """ + return self.makefile(".ini", tox=source) + + def getinicfg(self, source: str) -> SectionWrapper: + """Return the pytest section from the tox.ini config file.""" + p = self.makeini(source) + return IniConfig(str(p))["pytest"] + + def makepyprojecttoml(self, source: str) -> Path: + """Write a pyproject.toml file. + + :param source: The contents. + :returns: The pyproject.ini file. + + .. versionadded:: 6.0 + """ + return self.makefile(".toml", pyproject=source) + + def makepyfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .py extension. + + Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.py. + pytester.makepyfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.makepyfile(custom="foobar") + # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. + + """ + return self._makefile(".py", args, kwargs) + + def maketxtfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .txt extension. + + Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.txt. + pytester.maketxtfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.maketxtfile(custom="foobar") + # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. + + """ + return self._makefile(".txt", args, kwargs) + + def syspathinsert(self, path: str | os.PathLike[str] | None = None) -> None: + """Prepend a directory to sys.path, defaults to :attr:`path`. + + This is undone automatically when this object dies at the end of each + test. + + :param path: + The path. + """ + if path is None: + path = self.path + + self._monkeypatch.syspath_prepend(str(path)) + + def mkdir(self, name: str | os.PathLike[str]) -> Path: + """Create a new (sub)directory. + + :param name: + The name of the directory, relative to the pytester path. + :returns: + The created directory. + :rtype: pathlib.Path + """ + p = self.path / name + p.mkdir() + return p + + def mkpydir(self, name: str | os.PathLike[str]) -> Path: + """Create a new python package. + + This creates a (sub)directory with an empty ``__init__.py`` file so it + gets recognised as a Python package. + """ + p = self.path / name + p.mkdir() + p.joinpath("__init__.py").touch() + return p + + def copy_example(self, name: str | None = None) -> Path: + """Copy file from project's directory into the testdir. + + :param name: + The name of the file to copy. + :return: + Path to the copied directory (inside ``self.path``). + :rtype: pathlib.Path + """ + example_dir_ = self._request.config.getini("pytester_example_dir") + if example_dir_ is None: + raise ValueError("pytester_example_dir is unset, can't copy examples") + example_dir: Path = self._request.config.rootpath / example_dir_ + + for extra_element in self._request.node.iter_markers("pytester_example_path"): + assert extra_element.args + example_dir = example_dir.joinpath(*extra_element.args) + + if name is None: + func_name = self._name + maybe_dir = example_dir / func_name + maybe_file = example_dir / (func_name + ".py") + + if maybe_dir.is_dir(): + example_path = maybe_dir + elif maybe_file.is_file(): + example_path = maybe_file + else: + raise LookupError( + f"{func_name} can't be found as module or package in {example_dir}" + ) + else: + example_path = example_dir.joinpath(name) + + if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): + shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) + return self.path + elif example_path.is_file(): + result = self.path.joinpath(example_path.name) + shutil.copy(example_path, result) + return result + else: + raise LookupError( + f'example "{example_path}" is not found as a file or directory' + ) + + def getnode(self, config: Config, arg: str | os.PathLike[str]) -> Collector | Item: + """Get the collection node of a file. + + :param config: + A pytest config. + See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. + :param arg: + Path to the file. + :returns: + The node. + """ + session = Session.from_config(config) + assert "::" not in str(arg) + p = Path(os.path.abspath(arg)) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([str(p)], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def getpathnode(self, path: str | os.PathLike[str]) -> Collector | Item: + """Return the collection node of a file. + + This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to + create the (configured) pytest Config instance. + + :param path: + Path to the file. + :returns: + The node. + """ + path = Path(path) + config = self.parseconfigure(path) + session = Session.from_config(config) + x = bestrelpath(session.path, path) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([x], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def genitems(self, colitems: Sequence[Item | Collector]) -> list[Item]: + """Generate all test items from a collection node. + + This recurses into the collection node and returns a list of all the + test items contained within. + + :param colitems: + The collection nodes. + :returns: + The collected items. + """ + session = colitems[0].session + result: list[Item] = [] + for colitem in colitems: + result.extend(session.genitems(colitem)) + return result + + def runitem(self, source: str) -> Any: + """Run the "test_func" Item. + + The calling test instance (class containing the test method) must + provide a ``.getrunner()`` method which should return a runner which + can run the test protocol for a single item, e.g. + ``_pytest.runner.runtestprotocol``. + """ + # used from runner functional tests + item = self.getitem(source) + # the test class where we are called from wants to provide the runner + testclassinstance = self._request.instance + runner = testclassinstance.getrunner() + return runner(item) + + def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: + """Run a test module in process using ``pytest.main()``. + + This run writes "source" into a temporary file and runs + ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance + for the result. + + :param source: The source code of the test module. + :param cmdlineargs: Any extra command line arguments to use. + """ + p = self.makepyfile(source) + values = [*list(cmdlineargs), p] + return self.inline_run(*values) + + def inline_genitems(self, *args) -> tuple[list[Item], HookRecorder]: + """Run ``pytest.main(['--collect-only'])`` in-process. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself like :py:meth:`inline_run`, but returns a + tuple of the collected items and a :py:class:`HookRecorder` instance. + """ + rec = self.inline_run("--collect-only", *args) + items = [x.item for x in rec.getcalls("pytest_itemcollected")] + return items, rec + + def inline_run( + self, + *args: str | os.PathLike[str], + plugins=(), + no_reraise_ctrlc: bool = False, + ) -> HookRecorder: + """Run ``pytest.main()`` in-process, returning a HookRecorder. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself. This means it can return a + :py:class:`HookRecorder` instance which gives more detailed results + from that run than can be done by matching stdout/stderr from + :py:meth:`runpytest`. + + :param args: + Command line arguments to pass to :py:func:`pytest.main`. + :param plugins: + Extra plugin instances the ``pytest.main()`` instance should use. + :param no_reraise_ctrlc: + Typically we reraise keyboard interrupts from the child run. If + True, the KeyboardInterrupt exception is captured. + """ + from _pytest.unraisableexception import gc_collect_iterations_key + + # (maybe a cpython bug?) the importlib cache sometimes isn't updated + # properly between file creation and inline_run (especially if imports + # are interspersed with file creation) + importlib.invalidate_caches() + + plugins = list(plugins) + finalizers = [] + try: + # Any sys.module or sys.path changes done while running pytest + # inline should be reverted after the test run completes to avoid + # clashing with later inline tests run within the same pytest test, + # e.g. just because they use matching test module names. + finalizers.append(self.__take_sys_modules_snapshot().restore) + finalizers.append(SysPathsSnapshot().restore) + + # Important note: + # - our tests should not leave any other references/registrations + # laying around other than possibly loaded test modules + # referenced from sys.modules, as nothing will clean those up + # automatically + + rec = [] + + class PytesterHelperPlugin: + @staticmethod + def pytest_configure(config: Config) -> None: + rec.append(self.make_hook_recorder(config.pluginmanager)) + + # The unraisable plugin GC collect slows down inline + # pytester runs too much. + config.stash[gc_collect_iterations_key] = 0 + + plugins.append(PytesterHelperPlugin()) + ret = main([str(x) for x in args], plugins=plugins) + if len(rec) == 1: + reprec = rec.pop() + else: + + class reprec: # type: ignore + pass + + reprec.ret = ret + + # Typically we reraise keyboard interrupts from the child run + # because it's our user requesting interruption of the testing. + if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: + calls = reprec.getcalls("pytest_keyboard_interrupt") + if calls and calls[-1].excinfo.type == KeyboardInterrupt: + raise KeyboardInterrupt() + return reprec + finally: + for finalizer in finalizers: + finalizer() + + def runpytest_inprocess( + self, *args: str | os.PathLike[str], **kwargs: Any + ) -> RunResult: + """Return result of running pytest in-process, providing a similar + interface to what self.runpytest() provides.""" + syspathinsert = kwargs.pop("syspathinsert", False) + + if syspathinsert: + self.syspathinsert() + instant = timing.Instant() + capture = _get_multicapture("sys") + capture.start_capturing() + try: + try: + reprec = self.inline_run(*args, **kwargs) + except SystemExit as e: + ret = e.args[0] + try: + ret = ExitCode(e.args[0]) + except ValueError: + pass + + class reprec: # type: ignore + ret = ret + + except Exception: + traceback.print_exc() + + class reprec: # type: ignore + ret = ExitCode(3) + + finally: + out, err = capture.readouterr() + capture.stop_capturing() + sys.stdout.write(out) + sys.stderr.write(err) + + assert reprec.ret is not None + res = RunResult( + reprec.ret, out.splitlines(), err.splitlines(), instant.elapsed().seconds + ) + res.reprec = reprec # type: ignore + return res + + def runpytest(self, *args: str | os.PathLike[str], **kwargs: Any) -> RunResult: + """Run pytest inline or in a subprocess, depending on the command line + option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" + new_args = self._ensure_basetemp(args) + if self._method == "inprocess": + return self.runpytest_inprocess(*new_args, **kwargs) + elif self._method == "subprocess": + return self.runpytest_subprocess(*new_args, **kwargs) + raise RuntimeError(f"Unrecognized runpytest option: {self._method}") + + def _ensure_basetemp( + self, args: Sequence[str | os.PathLike[str]] + ) -> list[str | os.PathLike[str]]: + new_args = list(args) + for x in new_args: + if str(x).startswith("--basetemp"): + break + else: + new_args.append( + "--basetemp={}".format(self.path.parent.joinpath("basetemp")) + ) + return new_args + + def parseconfig(self, *args: str | os.PathLike[str]) -> Config: + """Return a new pytest :class:`pytest.Config` instance from given + commandline args. + + This invokes the pytest bootstrapping code in _pytest.config to create a + new :py:class:`pytest.PytestPluginManager` and call the + :hook:`pytest_cmdline_parse` hook to create a new :class:`pytest.Config` + instance. + + If :attr:`plugins` has been populated they should be plugin modules + to be registered with the plugin manager. + """ + import _pytest.config + + new_args = self._ensure_basetemp(args) + new_args = [str(x) for x in new_args] + + config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] + # we don't know what the test will do with this half-setup config + # object and thus we make sure it gets unconfigured properly in any + # case (otherwise capturing could still be active, for example) + self._request.addfinalizer(config._ensure_unconfigure) + return config + + def parseconfigure(self, *args: str | os.PathLike[str]) -> Config: + """Return a new pytest configured Config instance. + + Returns a new :py:class:`pytest.Config` instance like + :py:meth:`parseconfig`, but also calls the :hook:`pytest_configure` + hook. + """ + config = self.parseconfig(*args) + config._do_configure() + return config + + def getitem( + self, source: str | os.PathLike[str], funcname: str = "test_func" + ) -> Item: + """Return the test item for a test function. + + Writes the source to a python file and runs pytest's collection on + the resulting module, returning the test item for the requested + function name. + + :param source: + The module source. + :param funcname: + The name of the test function for which to return a test item. + :returns: + The test item. + """ + items = self.getitems(source) + for item in items: + if item.name == funcname: + return item + assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" + + def getitems(self, source: str | os.PathLike[str]) -> list[Item]: + """Return all test items collected from the module. + + Writes the source to a Python file and runs pytest's collection on + the resulting module, returning all test items contained within. + """ + modcol = self.getmodulecol(source) + return self.genitems([modcol]) + + def getmodulecol( + self, + source: str | os.PathLike[str], + configargs=(), + *, + withinit: bool = False, + ): + """Return the module collection node for ``source``. + + Writes ``source`` to a file using :py:meth:`makepyfile` and then + runs the pytest collection on it, returning the collection node for the + test module. + + :param source: + The source code of the module to collect. + + :param configargs: + Any extra arguments to pass to :py:meth:`parseconfigure`. + + :param withinit: + Whether to also write an ``__init__.py`` file to the same + directory to ensure it is a package. + """ + if isinstance(source, os.PathLike): + path = self.path.joinpath(source) + assert not withinit, "not supported for paths" + else: + kw = {self._name: str(source)} + path = self.makepyfile(**kw) + if withinit: + self.makepyfile(__init__="#") + self.config = config = self.parseconfigure(path, *configargs) + return self.getnode(config, path) + + def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: + """Return the collection node for name from the module collection. + + Searches a module collection node for a collection node matching the + given name. + + :param modcol: A module collection node; see :py:meth:`getmodulecol`. + :param name: The name of the node to return. + """ + if modcol not in self._mod_collections: + self._mod_collections[modcol] = list(modcol.collect()) + for colitem in self._mod_collections[modcol]: + if colitem.name == name: + return colitem + return None + + def popen( + self, + cmdargs: Sequence[str | os.PathLike[str]], + stdout: int | TextIO = subprocess.PIPE, + stderr: int | TextIO = subprocess.PIPE, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, + **kw, + ): + """Invoke :py:class:`subprocess.Popen`. + + Calls :py:class:`subprocess.Popen` making sure the current working + directory is in ``PYTHONPATH``. + + You probably want to use :py:meth:`run` instead. + """ + env = os.environ.copy() + env["PYTHONPATH"] = os.pathsep.join( + filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) + ) + kw["env"] = env + + if stdin is self.CLOSE_STDIN: + kw["stdin"] = subprocess.PIPE + elif isinstance(stdin, bytes): + kw["stdin"] = subprocess.PIPE + else: + kw["stdin"] = stdin + + popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) + if stdin is self.CLOSE_STDIN: + assert popen.stdin is not None + popen.stdin.close() + elif isinstance(stdin, bytes): + assert popen.stdin is not None + popen.stdin.write(stdin) + + return popen + + def run( + self, + *cmdargs: str | os.PathLike[str], + timeout: float | None = None, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, + ) -> RunResult: + """Run a command with arguments. + + Run a process using :py:class:`subprocess.Popen` saving the stdout and + stderr. + + :param cmdargs: + The sequence of arguments to pass to :py:class:`subprocess.Popen`, + with path-like objects being converted to :py:class:`str` + automatically. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :param stdin: + Optional standard input. + + - If it is ``CLOSE_STDIN`` (Default), then this method calls + :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and + the standard input is closed immediately after the new command is + started. + + - If it is of type :py:class:`bytes`, these bytes are sent to the + standard input of the command. + + - Otherwise, it is passed through to :py:class:`subprocess.Popen`. + For further information in this case, consult the document of the + ``stdin`` parameter in :py:class:`subprocess.Popen`. + :type stdin: _pytest.compat.NotSetType | bytes | IO[Any] | int + :returns: + The result. + + """ + __tracebackhide__ = True + + cmdargs = tuple(os.fspath(arg) for arg in cmdargs) + p1 = self.path.joinpath("stdout") + p2 = self.path.joinpath("stderr") + print("running:", *cmdargs) + print(" in:", Path.cwd()) + + with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: + instant = timing.Instant() + popen = self.popen( + cmdargs, + stdin=stdin, + stdout=f1, + stderr=f2, + close_fds=(sys.platform != "win32"), + ) + if popen.stdin is not None: + popen.stdin.close() + + def handle_timeout() -> None: + __tracebackhide__ = True + + timeout_message = f"{timeout} second timeout expired running: {cmdargs}" + + popen.kill() + popen.wait() + raise self.TimeoutExpired(timeout_message) + + if timeout is None: + ret = popen.wait() + else: + try: + ret = popen.wait(timeout) + except subprocess.TimeoutExpired: + handle_timeout() + + with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: + out = f1.read().splitlines() + err = f2.read().splitlines() + + self._dump_lines(out, sys.stdout) + self._dump_lines(err, sys.stderr) + + with contextlib.suppress(ValueError): + ret = ExitCode(ret) + return RunResult(ret, out, err, instant.elapsed().seconds) + + def _dump_lines(self, lines, fp): + try: + for line in lines: + print(line, file=fp) + except UnicodeEncodeError: + print(f"couldn't print to {fp} because of encoding") + + def _getpytestargs(self) -> tuple[str, ...]: + return sys.executable, "-mpytest" + + def runpython(self, script: os.PathLike[str]) -> RunResult: + """Run a python script using sys.executable as interpreter.""" + return self.run(sys.executable, script) + + def runpython_c(self, command: str) -> RunResult: + """Run ``python -c "command"``.""" + return self.run(sys.executable, "-c", command) + + def runpytest_subprocess( + self, *args: str | os.PathLike[str], timeout: float | None = None + ) -> RunResult: + """Run pytest as a subprocess with given arguments. + + Any plugins added to the :py:attr:`plugins` list will be added using the + ``-p`` command line option. Additionally ``--basetemp`` is used to put + any temporary files and directories in a numbered directory prefixed + with "runpytest-" to not conflict with the normal numbered pytest + location for temporary files and directories. + + :param args: + The sequence of arguments to pass to the pytest subprocess. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :returns: + The result. + """ + __tracebackhide__ = True + p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) + args = (f"--basetemp={p}", *args) + plugins = [x for x in self.plugins if isinstance(x, str)] + if plugins: + args = ("-p", plugins[0], *args) + args = self._getpytestargs() + args + return self.run(*args, timeout=timeout) + + def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """Run pytest using pexpect. + + This makes sure to use the right pytest and sets up the temporary + directory locations. + + The pexpect child is returned. + """ + basetemp = self.path / "temp-pexpect" + basetemp.mkdir(mode=0o700) + invoke = " ".join(map(str, self._getpytestargs())) + cmd = f"{invoke} --basetemp={basetemp} {string}" + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """Run a command using pexpect. + + The pexpect child is returned. + """ + pexpect = importorskip("pexpect", "3.0") + if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): + skip("pypy-64 bit not supported") + if not hasattr(pexpect, "spawn"): + skip("pexpect.spawn not available") + logfile = self.path.joinpath("spawn.out").open("wb") + + child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) + self._request.addfinalizer(logfile.close) + return child + + +class LineComp: + def __init__(self) -> None: + self.stringio = StringIO() + """:class:`python:io.StringIO()` instance used for input.""" + + def assert_contains_lines(self, lines2: Sequence[str]) -> None: + """Assert that ``lines2`` are contained (linearly) in :attr:`stringio`'s value. + + Lines are matched using :func:`LineMatcher.fnmatch_lines `. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) + self.stringio.seek(0) + lines1 = val.split("\n") + LineMatcher(lines1).fnmatch_lines(lines2) + + +class LineMatcher: + """Flexible matching of text. + + This is a convenience class to test large texts like the output of + commands. + + The constructor takes a list of lines without their trailing newlines, i.e. + ``text.splitlines()``. + """ + + def __init__(self, lines: list[str]) -> None: + self.lines = lines + self._log_output: list[str] = [] + + def __str__(self) -> str: + """Return the entire original text. + + .. versionadded:: 6.2 + You can use :meth:`str` in older versions. + """ + return "\n".join(self.lines) + + def _getlines(self, lines2: str | Sequence[str] | Source) -> Sequence[str]: + if isinstance(lines2, str): + lines2 = Source(lines2) + if isinstance(lines2, Source): + lines2 = lines2.strip().lines + return lines2 + + def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, fnmatch) + + def re_match_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:re.match`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) + + def _match_lines_random( + self, lines2: Sequence[str], match_func: Callable[[str, str], bool] + ) -> None: + __tracebackhide__ = True + lines2 = self._getlines(lines2) + for line in lines2: + for x in self.lines: + if line == x or match_func(x, line): + self._log("matched: ", repr(line)) + break + else: + msg = f"line {line!r} not found in output" + self._log(msg) + self._fail(msg) + + def get_lines_after(self, fnline: str) -> Sequence[str]: + """Return all lines following the given line in the text. + + The given line can contain glob wildcards. + """ + for i, line in enumerate(self.lines): + if fnline == line or fnmatch(line, fnline): + return self.lines[i + 1 :] + raise ValueError(f"line {fnline!r} not found in output") + + def _log(self, *args) -> None: + self._log_output.append(" ".join(str(x) for x in args)) + + @property + def _log_text(self) -> str: + return "\n".join(self._log_output) + + def fnmatch_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). + + The argument is a list of lines which have to match and can use glob + wildcards. If they do not match a pytest.fail() is called. The + matches and non-matches are also shown as part of the error message. + + :param lines2: String patterns to match. + :param consecutive: Match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) + + def re_match_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:re.match`). + + The argument is a list of lines which have to match using ``re.match``. + If they do not match a pytest.fail() is called. + + The matches and non-matches are also shown as part of the error message. + + :param lines2: string patterns to match. + :param consecutive: match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines( + lines2, + lambda name, pat: bool(re.match(pat, name)), + "re.match", + consecutive=consecutive, + ) + + def _match_lines( + self, + lines2: Sequence[str], + match_func: Callable[[str, str], bool], + match_nickname: str, + *, + consecutive: bool = False, + ) -> None: + """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``. + + :param Sequence[str] lines2: + List of string patterns to match. The actual format depends on + ``match_func``. + :param match_func: + A callable ``match_func(line, pattern)`` where line is the + captured line from stdout/stderr and pattern is the matching + pattern. + :param str match_nickname: + The nickname for the match function that will be logged to stdout + when a match occurs. + :param consecutive: + Match lines consecutively? + """ + if not isinstance(lines2, collections.abc.Sequence): + raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") + lines2 = self._getlines(lines2) + lines1 = self.lines[:] + extralines = [] + __tracebackhide__ = True + wnick = len(match_nickname) + 1 + started = False + for line in lines2: + nomatchprinted = False + while lines1: + nextline = lines1.pop(0) + if line == nextline: + self._log("exact match:", repr(line)) + started = True + break + elif match_func(nextline, line): + self._log(f"{match_nickname}:", repr(line)) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + started = True + break + else: + if consecutive and started: + msg = f"no consecutive match: {line!r}" + self._log(msg) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + self._fail(msg) + if not nomatchprinted: + self._log( + "{:>{width}}".format("nomatch:", width=wnick), repr(line) + ) + nomatchprinted = True + self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) + extralines.append(nextline) + else: + msg = f"remains unmatched: {line!r}" + self._log(msg) + self._fail(msg) + self._log_output = [] + + def no_fnmatch_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + self._no_match_line(pat, fnmatch, "fnmatch") + + def no_re_match_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``re.match``. + + :param str pat: The regular expression to match lines. + """ + __tracebackhide__ = True + self._no_match_line( + pat, lambda name, pat: bool(re.match(pat, name)), "re.match" + ) + + def _no_match_line( + self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str + ) -> None: + """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + nomatch_printed = False + wnick = len(match_nickname) + 1 + for line in self.lines: + if match_func(line, pat): + msg = f"{match_nickname}: {pat!r}" + self._log(msg) + self._log("{:>{width}}".format("with:", width=wnick), repr(line)) + self._fail(msg) + else: + if not nomatch_printed: + self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) + nomatch_printed = True + self._log("{:>{width}}".format("and:", width=wnick), repr(line)) + self._log_output = [] + + def _fail(self, msg: str) -> None: + __tracebackhide__ = True + log_text = self._log_text + self._log_output = [] + fail(log_text) + + def str(self) -> str: + """Return the entire original text.""" + return str(self) diff --git a/.venv/lib/python3.11/site-packages/_pytest/pytester_assertions.py b/.venv/lib/python3.11/site-packages/_pytest/pytester_assertions.py new file mode 100644 index 00000000..915cc8a1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/pytester_assertions.py @@ -0,0 +1,74 @@ +"""Helper plugin for pytester; should not be loaded on its own.""" + +# This plugin contains assertions used by pytester. pytester cannot +# contain them itself, since it is imported by the `pytest` module, +# hence cannot be subject to assertion rewriting, which requires a +# module to not be already imported. +from __future__ import annotations + +from collections.abc import Sequence + +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +def assertoutcome( + outcomes: tuple[ + Sequence[TestReport], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], + ], + passed: int = 0, + skipped: int = 0, + failed: int = 0, +) -> None: + __tracebackhide__ = True + + realpassed, realskipped, realfailed = outcomes + obtained = { + "passed": len(realpassed), + "skipped": len(realskipped), + "failed": len(realfailed), + } + expected = {"passed": passed, "skipped": skipped, "failed": failed} + assert obtained == expected, outcomes + + +def assert_outcomes( + outcomes: dict[str, int], + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + warnings: int | None = None, + deselected: int | None = None, +) -> None: + """Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + __tracebackhide__ = True + + obtained = { + "passed": outcomes.get("passed", 0), + "skipped": outcomes.get("skipped", 0), + "failed": outcomes.get("failed", 0), + "errors": outcomes.get("errors", 0), + "xpassed": outcomes.get("xpassed", 0), + "xfailed": outcomes.get("xfailed", 0), + } + expected = { + "passed": passed, + "skipped": skipped, + "failed": failed, + "errors": errors, + "xpassed": xpassed, + "xfailed": xfailed, + } + if warnings is not None: + obtained["warnings"] = outcomes.get("warnings", 0) + expected["warnings"] = warnings + if deselected is not None: + obtained["deselected"] = outcomes.get("deselected", 0) + expected["deselected"] = deselected + assert obtained == expected diff --git a/.venv/lib/python3.11/site-packages/_pytest/python.py b/.venv/lib/python3.11/site-packages/_pytest/python.py new file mode 100644 index 00000000..8e4fb041 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/python.py @@ -0,0 +1,1723 @@ +# mypy: allow-untyped-defs +"""Python test discovery, setup and run of test functions.""" + +from __future__ import annotations + +import abc +from collections import Counter +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import enum +import fnmatch +from functools import partial +import inspect +import itertools +import os +from pathlib import Path +import re +import types +from typing import Any +from typing import final +from typing import Literal +from typing import NoReturn +from typing import TYPE_CHECKING +import warnings + +import _pytest +from _pytest import fixtures +from _pytest import nodes +from _pytest._code import filter_traceback +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback +from _pytest._io.saferepr import saferepr +from _pytest.compat import ascii_escaped +from _pytest.compat import get_default_arg_names +from _pytest.compat import get_real_func +from _pytest.compat import getimfunc +from _pytest.compat import is_async_function +from _pytest.compat import LEGACY_PATH +from _pytest.compat import NOTSET +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import FixtureRequest +from _pytest.fixtures import FuncFixtureInfo +from _pytest.fixtures import get_scope_node +from _pytest.main import Session +from _pytest.mark import ParameterSet +from _pytest.mark.structures import _HiddenParam +from _pytest.mark.structures import get_unpacked_marks +from _pytest.mark.structures import HIDDEN_PARAM +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import normalize_mark_list +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.pathlib import fnmatch_ex +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportPathMismatchError +from _pytest.pathlib import scandir +from _pytest.scope import _ScopeName +from _pytest.scope import Scope +from _pytest.stash import StashKey +from _pytest.warning_types import PytestCollectionWarning +from _pytest.warning_types import PytestReturnNotNoneWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "python_files", + type="args", + # NOTE: default is also used in AssertionRewritingHook. + default=["test_*.py", "*_test.py"], + help="Glob-style file patterns for Python test module discovery", + ) + parser.addini( + "python_classes", + type="args", + default=["Test"], + help="Prefixes or glob names for Python test class discovery", + ) + parser.addini( + "python_functions", + type="args", + default=["test"], + help="Prefixes or glob names for Python test function and method discovery", + ) + parser.addini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", + type="bool", + default=False, + help="Disable string escape non-ASCII characters, might cause unwanted " + "side effects(use at your own risk)", + ) + + +def pytest_generate_tests(metafunc: Metafunc) -> None: + for marker in metafunc.definition.iter_markers(name="parametrize"): + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + + +def pytest_configure(config: Config) -> None: + config.addinivalue_line( + "markers", + "parametrize(argnames, argvalues): call a test function multiple " + "times passing in different arguments in turn. argvalues generally " + "needs to be a list of values if argnames specifies only one name " + "or a list of tuples of values if argnames specifies multiple names. " + "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " + "decorated test function, one with arg1=1 and another with arg1=2." + "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info " + "and examples.", + ) + config.addinivalue_line( + "markers", + "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " + "all of the specified fixtures. see " + "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ", + ) + + +def async_fail(nodeid: str) -> None: + msg = ( + "async def functions are not natively supported.\n" + "You need to install a suitable plugin for your async framework, for example:\n" + " - anyio\n" + " - pytest-asyncio\n" + " - pytest-tornasync\n" + " - pytest-trio\n" + " - pytest-twisted" + ) + fail(msg, pytrace=False) + + +@hookimpl(trylast=True) +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: + testfunction = pyfuncitem.obj + if is_async_function(testfunction): + async_fail(pyfuncitem.nodeid) + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + result = testfunction(**testargs) + if hasattr(result, "__await__") or hasattr(result, "__aiter__"): + async_fail(pyfuncitem.nodeid) + elif result is not None: + warnings.warn( + PytestReturnNotNoneWarning( + f"Test functions should return None, but {pyfuncitem.nodeid} returned {type(result)!r}.\n" + "Did you mean to use `assert` instead of `return`?\n" + "See https://docs.pytest.org/en/stable/how-to/assert.html#return-not-none for more information." + ) + ) + return True + + +def pytest_collect_directory( + path: Path, parent: nodes.Collector +) -> nodes.Collector | None: + pkginit = path / "__init__.py" + try: + has_pkginit = pkginit.is_file() + except PermissionError: + # See https://github.com/pytest-dev/pytest/issues/12120#issuecomment-2106349096. + return None + if has_pkginit: + return Package.from_parent(parent, path=path) + return None + + +def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Module | None: + if file_path.suffix == ".py": + if not parent.session.isinitpath(file_path): + if not path_matches_patterns( + file_path, parent.config.getini("python_files") + ): + return None + ihook = parent.session.gethookproxy(file_path) + module: Module = ihook.pytest_pycollect_makemodule( + module_path=file_path, parent=parent + ) + return module + return None + + +def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: + """Return whether path matches any of the patterns in the list of globs given.""" + return any(fnmatch_ex(pattern, path) for pattern in patterns) + + +def pytest_pycollect_makemodule(module_path: Path, parent) -> Module: + return Module.from_parent(parent, path=module_path) + + +@hookimpl(trylast=True) +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> None | nodes.Item | nodes.Collector | list[nodes.Item | nodes.Collector]: + assert isinstance(collector, (Class, Module)), type(collector) + # Nothing was collected elsewhere, let's do it here. + if safe_isclass(obj): + if collector.istestclass(obj, name): + return Class.from_parent(collector, name=name, obj=obj) + elif collector.istestfunction(obj, name): + # mock seems to store unbound methods (issue473), normalize it. + obj = getattr(obj, "__func__", obj) + # We need to try and unwrap the function if it's a functools.partial + # or a functools.wrapped. + # We mustn't if it's been wrapped with mock.patch (python 2 only). + if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))): + filename, lineno = getfslineno(obj) + warnings.warn_explicit( + message=PytestCollectionWarning( + f"cannot collect {name!r} because it is not a function." + ), + category=None, + filename=str(filename), + lineno=lineno + 1, + ) + elif getattr(obj, "__test__", True): + if inspect.isgeneratorfunction(obj): + fail( + f"'yield' keyword is allowed in fixtures, but not in tests ({name})", + pytrace=False, + ) + return list(collector._genfunctions(name, obj)) + return None + return None + + +class PyobjMixin(nodes.Node): + """this mix-in inherits from Node to carry over the typing information + + as its intended to always mix in before a node + its position in the mro is unaffected""" + + _ALLOW_MARKERS = True + + @property + def module(self): + """Python module object this node was collected from (can be None).""" + node = self.getparent(Module) + return node.obj if node is not None else None + + @property + def cls(self): + """Python class object this node was collected from (can be None).""" + node = self.getparent(Class) + return node.obj if node is not None else None + + @property + def instance(self): + """Python instance object the function is bound to. + + Returns None if not a test method, e.g. for a standalone test function, + a class or a module. + """ + # Overridden by Function. + return None + + @property + def obj(self): + """Underlying Python object.""" + obj = getattr(self, "_obj", None) + if obj is None: + self._obj = obj = self._getobj() + # XXX evil hack + # used to avoid Function marker duplication + if self._ALLOW_MARKERS: + self.own_markers.extend(get_unpacked_marks(self.obj)) + # This assumes that `obj` is called before there is a chance + # to add custom keys to `self.keywords`, so no fear of overriding. + self.keywords.update((mark.name, mark) for mark in self.own_markers) + return obj + + @obj.setter + def obj(self, value): + self._obj = value + + def _getobj(self): + """Get the underlying Python object. May be overwritten by subclasses.""" + # TODO: Improve the type of `parent` such that assert/ignore aren't needed. + assert self.parent is not None + obj = self.parent.obj # type: ignore[attr-defined] + return getattr(obj, self.name) + + def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str: + """Return Python path relative to the containing module.""" + parts = [] + for node in self.iter_parents(): + name = node.name + if isinstance(node, Module): + name = os.path.splitext(name)[0] + if stopatmodule: + if includemodule: + parts.append(name) + break + parts.append(name) + parts.reverse() + return ".".join(parts) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + # XXX caching? + path, lineno = getfslineno(self.obj) + modpath = self.getmodpath() + return path, lineno, modpath + + +# As an optimization, these builtin attribute names are pre-ignored when +# iterating over an object during collection -- the pytest_pycollect_makeitem +# hook is not called for them. +# fmt: off +class _EmptyClass: pass # noqa: E701 +IGNORED_ATTRIBUTES = frozenset.union( + frozenset(), + # Module. + dir(types.ModuleType("empty_module")), + # Some extra module attributes the above doesn't catch. + {"__builtins__", "__file__", "__cached__"}, + # Class. + dir(_EmptyClass), + # Instance. + dir(_EmptyClass()), +) +del _EmptyClass +# fmt: on + + +class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): + def funcnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_functions", name) + + def isnosetest(self, obj: object) -> bool: + """Look for the __test__ attribute, which is applied by the + @nose.tools.istest decorator. + """ + # We explicitly check for "is True" here to not mistakenly treat + # classes with a custom __getattr__ returning something truthy (like a + # function) as test classes. + return safe_getattr(obj, "__test__", False) is True + + def classnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_classes", name) + + def istestfunction(self, obj: object, name: str) -> bool: + if self.funcnamefilter(name) or self.isnosetest(obj): + if isinstance(obj, (staticmethod, classmethod)): + # staticmethods and classmethods need to be unwrapped. + obj = safe_getattr(obj, "__func__", False) + return callable(obj) and fixtures.getfixturemarker(obj) is None + else: + return False + + def istestclass(self, obj: object, name: str) -> bool: + if not (self.classnamefilter(name) or self.isnosetest(obj)): + return False + if inspect.isabstract(obj): + return False + return True + + def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool: + """Check if the given name matches the prefix or glob-pattern defined + in ini configuration.""" + for option in self.config.getini(option_name): + if name.startswith(option): + return True + # Check that name looks like a glob-string before calling fnmatch + # because this is called for every name in each collected module, + # and fnmatch is somewhat expensive to call. + elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( + name, option + ): + return True + return False + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not getattr(self.obj, "__test__", True): + return [] + + # Avoid random getattrs and peek in the __dict__ instead. + dicts = [getattr(self.obj, "__dict__", {})] + if isinstance(self.obj, type): + for basecls in self.obj.__mro__: + dicts.append(basecls.__dict__) + + # In each class, nodes should be definition ordered. + # __dict__ is definition ordered. + seen: set[str] = set() + dict_values: list[list[nodes.Item | nodes.Collector]] = [] + collect_imported_tests = self.session.config.getini("collect_imported_tests") + ihook = self.ihook + for dic in dicts: + values: list[nodes.Item | nodes.Collector] = [] + # Note: seems like the dict can change during iteration - + # be careful not to remove the list() without consideration. + for name, obj in list(dic.items()): + if name in IGNORED_ATTRIBUTES: + continue + if name in seen: + continue + seen.add(name) + + if not collect_imported_tests and isinstance(self, Module): + # Do not collect functions and classes from other modules. + if inspect.isfunction(obj) or inspect.isclass(obj): + if obj.__module__ != self._getobj().__name__: + continue + + res = ihook.pytest_pycollect_makeitem( + collector=self, name=name, obj=obj + ) + if res is None: + continue + elif isinstance(res, list): + values.extend(res) + else: + values.append(res) + dict_values.append(values) + + # Between classes in the class hierarchy, reverse-MRO order -- nodes + # inherited from base classes should come before subclasses. + result = [] + for values in reversed(dict_values): + result.extend(values) + return result + + def _genfunctions(self, name: str, funcobj) -> Iterator[Function]: + modulecol = self.getparent(Module) + assert modulecol is not None + module = modulecol.obj + clscol = self.getparent(Class) + cls = (clscol and clscol.obj) or None + + definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) + fixtureinfo = definition._fixtureinfo + + # pytest_generate_tests impls call metafunc.parametrize() which fills + # metafunc._calls, the outcome of the hook. + metafunc = Metafunc( + definition=definition, + fixtureinfo=fixtureinfo, + config=self.config, + cls=cls, + module=module, + _ispytest=True, + ) + methods = [] + if hasattr(module, "pytest_generate_tests"): + methods.append(module.pytest_generate_tests) + if cls is not None and hasattr(cls, "pytest_generate_tests"): + methods.append(cls().pytest_generate_tests) + self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) + + if not metafunc._calls: + yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) + else: + metafunc._recompute_direct_params_indices() + # Direct parametrizations taking place in module/class-specific + # `metafunc.parametrize` calls may have shadowed some fixtures, so make sure + # we update what the function really needs a.k.a its fixture closure. Note that + # direct parametrizations using `@pytest.mark.parametrize` have already been considered + # into making the closure using `ignore_args` arg to `getfixtureclosure`. + fixtureinfo.prune_dependency_tree() + + for callspec in metafunc._calls: + subname = f"{name}[{callspec.id}]" if callspec._idlist else name + yield Function.from_parent( + self, + name=subname, + callspec=callspec, + fixtureinfo=fixtureinfo, + keywords={callspec.id: True}, + originalname=name, + ) + + +def importtestmodule( + path: Path, + config: Config, +): + # We assume we are only called once per module. + importmode = config.getoption("--import-mode") + try: + mod = import_path( + path, + mode=importmode, + root=config.rootpath, + consider_namespace_packages=config.getini("consider_namespace_packages"), + ) + except SyntaxError as e: + raise nodes.Collector.CollectError( + ExceptionInfo.from_current().getrepr(style="short") + ) from e + except ImportPathMismatchError as e: + raise nodes.Collector.CollectError( + "import file mismatch:\n" + "imported module {!r} has this __file__ attribute:\n" + " {}\n" + "which is not the same as the test file we want to collect:\n" + " {}\n" + "HINT: remove __pycache__ / .pyc files and/or use a " + "unique basename for your test file modules".format(*e.args) + ) from e + except ImportError as e: + exc_info = ExceptionInfo.from_current() + if config.get_verbosity() < 2: + exc_info.traceback = exc_info.traceback.filter(filter_traceback) + exc_repr = ( + exc_info.getrepr(style="short") + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + raise nodes.Collector.CollectError( + f"ImportError while importing test module '{path}'.\n" + "Hint: make sure your test modules/packages have valid Python names.\n" + "Traceback:\n" + f"{formatted_tb}" + ) from e + except skip.Exception as e: + if e.allow_module_level: + raise + raise nodes.Collector.CollectError( + "Using pytest.skip outside of a test will skip the entire module. " + "If that's your intention, pass `allow_module_level=True`. " + "If you want to skip a specific test or an entire class, " + "use the @pytest.mark.skip or @pytest.mark.skipif decorators." + ) from e + config.pluginmanager.consider_module(mod) + return mod + + +class Module(nodes.File, PyCollector): + """Collector for test classes and functions in a Python module.""" + + def _getobj(self): + return importtestmodule(self.path, self.config) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + self._register_setup_module_fixture() + self._register_setup_function_fixture() + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def _register_setup_module_fixture(self) -> None: + """Register an autouse, module-scoped fixture for the collected module object + that invokes setUpModule/tearDownModule if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_module = _get_first_non_fixture_func( + self.obj, ("setUpModule", "setup_module") + ) + teardown_module = _get_first_non_fixture_func( + self.obj, ("tearDownModule", "teardown_module") + ) + + if setup_module is None and teardown_module is None: + return + + def xunit_setup_module_fixture(request) -> Generator[None]: + module = request.module + if setup_module is not None: + _call_with_optional_argument(setup_module, module) + yield + if teardown_module is not None: + _call_with_optional_argument(teardown_module, module) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_module_fixture_{self.obj.__name__}", + func=xunit_setup_module_fixture, + nodeid=self.nodeid, + scope="module", + autouse=True, + ) + + def _register_setup_function_fixture(self) -> None: + """Register an autouse, function-scoped fixture for the collected module object + that invokes setup_function/teardown_function if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) + teardown_function = _get_first_non_fixture_func( + self.obj, ("teardown_function",) + ) + if setup_function is None and teardown_function is None: + return + + def xunit_setup_function_fixture(request) -> Generator[None]: + if request.instance is not None: + # in this case we are bound to an instance, so we need to let + # setup_method handle this + yield + return + function = request.function + if setup_function is not None: + _call_with_optional_argument(setup_function, function) + yield + if teardown_function is not None: + _call_with_optional_argument(teardown_function, function) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_function_fixture_{self.obj.__name__}", + func=xunit_setup_function_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +class Package(nodes.Directory): + """Collector for files and directories in a Python packages -- directories + with an `__init__.py` file. + + .. note:: + + Directories without an `__init__.py` file are instead collected by + :class:`~pytest.Dir` by default. Both are :class:`~pytest.Directory` + collectors. + + .. versionchanged:: 8.0 + + Now inherits from :class:`~pytest.Directory`. + """ + + def __init__( + self, + fspath: LEGACY_PATH | None, + parent: nodes.Collector, + # NOTE: following args are unused: + config=None, + session=None, + nodeid=None, + path: Path | None = None, + ) -> None: + # NOTE: Could be just the following, but kept as-is for compat. + # super().__init__(self, fspath, parent=parent) + session = parent.session + super().__init__( + fspath=fspath, + path=path, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + ) + + def setup(self) -> None: + init_mod = importtestmodule(self.path / "__init__.py", self.config) + + # Not using fixtures to call setup_module here because autouse fixtures + # from packages are not called automatically (#4085). + setup_module = _get_first_non_fixture_func( + init_mod, ("setUpModule", "setup_module") + ) + if setup_module is not None: + _call_with_optional_argument(setup_module, init_mod) + + teardown_module = _get_first_non_fixture_func( + init_mod, ("tearDownModule", "teardown_module") + ) + if teardown_module is not None: + func = partial(_call_with_optional_argument, teardown_module, init_mod) + self.addfinalizer(func) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + # Always collect __init__.py first. + def sort_key(entry: os.DirEntry[str]) -> object: + return (entry.name != "__init__.py", entry.name) + + config = self.config + col: nodes.Collector | None + cols: Sequence[nodes.Collector] + ihook = self.ihook + for direntry in scandir(self.path, sort_key): + if direntry.is_dir(): + path = Path(direntry.path) + if not self.session.isinitpath(path, with_parents=True): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + col = ihook.pytest_collect_directory(path=path, parent=self) + if col is not None: + yield col + + elif direntry.is_file(): + path = Path(direntry.path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + cols = ihook.pytest_collect_file(file_path=path, parent=self) + yield from cols + + +def _call_with_optional_argument(func, arg) -> None: + """Call the given function with the given argument if func accepts one argument, otherwise + calls func without arguments.""" + arg_count = func.__code__.co_argcount + if inspect.ismethod(func): + arg_count -= 1 + if arg_count: + func(arg) + else: + func() + + +def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> object | None: + """Return the attribute from the given object to be used as a setup/teardown + xunit-style function, but only if not marked as a fixture to avoid calling it twice. + """ + for name in names: + meth: object | None = getattr(obj, name, None) + if meth is not None and fixtures.getfixturemarker(meth) is None: + return meth + return None + + +class Class(PyCollector): + """Collector for test methods (and nested classes) in a Python class.""" + + @classmethod + def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: # type: ignore[override] + """The public constructor.""" + return super().from_parent(name=name, parent=parent, **kw) + + def newinstance(self): + return self.obj() + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not safe_getattr(self.obj, "__test__", True): + return [] + if hasinit(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__init__ constructor (from: {self.parent.nodeid})" + ) + ) + return [] + elif hasnew(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__new__ constructor (from: {self.parent.nodeid})" + ) + ) + return [] + + self._register_setup_class_fixture() + self._register_setup_method_fixture() + + self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) + + return super().collect() + + def _register_setup_class_fixture(self) -> None: + """Register an autouse, class scoped fixture into the collected class object + that invokes setup_class/teardown_class if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) + teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",)) + if setup_class is None and teardown_class is None: + return + + def xunit_setup_class_fixture(request) -> Generator[None]: + cls = request.cls + if setup_class is not None: + func = getimfunc(setup_class) + _call_with_optional_argument(func, cls) + yield + if teardown_class is not None: + func = getimfunc(teardown_class) + _call_with_optional_argument(func, cls) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", + func=xunit_setup_class_fixture, + nodeid=self.nodeid, + scope="class", + autouse=True, + ) + + def _register_setup_method_fixture(self) -> None: + """Register an autouse, function scoped fixture into the collected class object + that invokes setup_method/teardown_method if either or both are available. + + Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_name = "setup_method" + setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) + teardown_name = "teardown_method" + teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) + if setup_method is None and teardown_method is None: + return + + def xunit_setup_method_fixture(request) -> Generator[None]: + instance = request.instance + method = request.function + if setup_method is not None: + func = getattr(instance, setup_name) + _call_with_optional_argument(func, method) + yield + if teardown_method is not None: + func = getattr(instance, teardown_name) + _call_with_optional_argument(func, method) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", + func=xunit_setup_method_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +def hasinit(obj: object) -> bool: + init: object = getattr(obj, "__init__", None) + if init: + return init != object.__init__ + return False + + +def hasnew(obj: object) -> bool: + new: object = getattr(obj, "__new__", None) + if new: + return new != object.__new__ + return False + + +@final +@dataclasses.dataclass(frozen=True) +class IdMaker: + """Make IDs for a parametrization.""" + + __slots__ = ( + "argnames", + "config", + "func_name", + "idfn", + "ids", + "nodeid", + "parametersets", + ) + + # The argnames of the parametrization. + argnames: Sequence[str] + # The ParameterSets of the parametrization. + parametersets: Sequence[ParameterSet] + # Optionally, a user-provided callable to make IDs for parameters in a + # ParameterSet. + idfn: Callable[[Any], object | None] | None + # Optionally, explicit IDs for ParameterSets by index. + ids: Sequence[object | None] | None + # Optionally, the pytest config. + # Used for controlling ASCII escaping, and for calling the + # :hook:`pytest_make_parametrize_id` hook. + config: Config | None + # Optionally, the ID of the node being parametrized. + # Used only for clearer error messages. + nodeid: str | None + # Optionally, the ID of the function being parametrized. + # Used only for clearer error messages. + func_name: str | None + + def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]: + """Make a unique identifier for each ParameterSet, that may be used to + identify the parametrization in a node ID. + + Format is -...-[counter], where prm_x_token is + - user-provided id, if given + - else an id derived from the value, applicable for certain types + - else + The counter suffix is appended only in case a string wouldn't be unique + otherwise. + """ + resolved_ids = list(self._resolve_ids()) + # All IDs must be unique! + if len(resolved_ids) != len(set(resolved_ids)): + # Record the number of occurrences of each ID. + id_counts = Counter(resolved_ids) + # Map the ID to its next suffix. + id_suffixes: dict[str, int] = defaultdict(int) + # Suffix non-unique IDs to make them unique. + for index, id in enumerate(resolved_ids): + if id_counts[id] > 1: + if id is HIDDEN_PARAM: + self._complain_multiple_hidden_parameter_sets() + suffix = "" + if id and id[-1].isdigit(): + suffix = "_" + new_id = f"{id}{suffix}{id_suffixes[id]}" + while new_id in set(resolved_ids): + id_suffixes[id] += 1 + new_id = f"{id}{suffix}{id_suffixes[id]}" + resolved_ids[index] = new_id + id_suffixes[id] += 1 + assert len(resolved_ids) == len(set(resolved_ids)), ( + f"Internal error: {resolved_ids=}" + ) + return resolved_ids + + def _resolve_ids(self) -> Iterable[str | _HiddenParam]: + """Resolve IDs for all ParameterSets (may contain duplicates).""" + for idx, parameterset in enumerate(self.parametersets): + if parameterset.id is not None: + # ID provided directly - pytest.param(..., id="...") + if parameterset.id is HIDDEN_PARAM: + yield HIDDEN_PARAM + else: + yield _ascii_escaped_by_config(parameterset.id, self.config) + elif self.ids and idx < len(self.ids) and self.ids[idx] is not None: + # ID provided in the IDs list - parametrize(..., ids=[...]). + if self.ids[idx] is HIDDEN_PARAM: + yield HIDDEN_PARAM + else: + yield self._idval_from_value_required(self.ids[idx], idx) + else: + # ID not provided - generate it. + yield "-".join( + self._idval(val, argname, idx) + for val, argname in zip(parameterset.values, self.argnames) + ) + + def _idval(self, val: object, argname: str, idx: int) -> str: + """Make an ID for a parameter in a ParameterSet.""" + idval = self._idval_from_function(val, argname, idx) + if idval is not None: + return idval + idval = self._idval_from_hook(val, argname) + if idval is not None: + return idval + idval = self._idval_from_value(val) + if idval is not None: + return idval + return self._idval_from_argname(argname, idx) + + def _idval_from_function(self, val: object, argname: str, idx: int) -> str | None: + """Try to make an ID for a parameter in a ParameterSet using the + user-provided id callable, if given.""" + if self.idfn is None: + return None + try: + id = self.idfn(val) + except Exception as e: + prefix = f"{self.nodeid}: " if self.nodeid is not None else "" + msg = "error raised while trying to determine id of parameter '{}' at position {}" + msg = prefix + msg.format(argname, idx) + raise ValueError(msg) from e + if id is None: + return None + return self._idval_from_value(id) + + def _idval_from_hook(self, val: object, argname: str) -> str | None: + """Try to make an ID for a parameter in a ParameterSet by calling the + :hook:`pytest_make_parametrize_id` hook.""" + if self.config: + id: str | None = self.config.hook.pytest_make_parametrize_id( + config=self.config, val=val, argname=argname + ) + return id + return None + + def _idval_from_value(self, val: object) -> str | None: + """Try to make an ID for a parameter in a ParameterSet from its value, + if the value type is supported.""" + if isinstance(val, (str, bytes)): + return _ascii_escaped_by_config(val, self.config) + elif val is None or isinstance(val, (float, int, bool, complex)): + return str(val) + elif isinstance(val, re.Pattern): + return ascii_escaped(val.pattern) + elif val is NOTSET: + # Fallback to default. Note that NOTSET is an enum.Enum. + pass + elif isinstance(val, enum.Enum): + return str(val) + elif isinstance(getattr(val, "__name__", None), str): + # Name of a class, function, module, etc. + name: str = getattr(val, "__name__") + return name + return None + + def _idval_from_value_required(self, val: object, idx: int) -> str: + """Like _idval_from_value(), but fails if the type is not supported.""" + id = self._idval_from_value(val) + if id is not None: + return id + + # Fail. + prefix = self._make_error_prefix() + msg = ( + f"{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. " + "Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__." + ) + fail(msg, pytrace=False) + + @staticmethod + def _idval_from_argname(argname: str, idx: int) -> str: + """Make an ID for a parameter in a ParameterSet from the argument name + and the index of the ParameterSet.""" + return str(argname) + str(idx) + + def _complain_multiple_hidden_parameter_sets(self) -> NoReturn: + fail( + f"{self._make_error_prefix()}multiple instances of HIDDEN_PARAM " + "cannot be used in the same parametrize call, " + "because the tests names need to be unique." + ) + + def _make_error_prefix(self) -> str: + if self.func_name is not None: + return f"In {self.func_name}: " + elif self.nodeid is not None: + return f"In {self.nodeid}: " + else: + return "" + + +@final +@dataclasses.dataclass(frozen=True) +class CallSpec2: + """A planned parameterized invocation of a test function. + + Calculated during collection for a given test function's Metafunc. + Once collection is over, each callspec is turned into a single Item + and stored in item.callspec. + """ + + # arg name -> arg value which will be passed to a fixture or pseudo-fixture + # of the same name. (indirect or direct parametrization respectively) + params: dict[str, object] = dataclasses.field(default_factory=dict) + # arg name -> arg index. + indices: dict[str, int] = dataclasses.field(default_factory=dict) + # arg name -> parameter scope. + # Used for sorting parametrized resources. + _arg2scope: Mapping[str, Scope] = dataclasses.field(default_factory=dict) + # Parts which will be added to the item's name in `[..]` separated by "-". + _idlist: Sequence[str] = dataclasses.field(default_factory=tuple) + # Marks which will be applied to the item. + marks: list[Mark] = dataclasses.field(default_factory=list) + + def setmulti( + self, + *, + argnames: Iterable[str], + valset: Iterable[object], + id: str | _HiddenParam, + marks: Iterable[Mark | MarkDecorator], + scope: Scope, + param_index: int, + nodeid: str, + ) -> CallSpec2: + params = self.params.copy() + indices = self.indices.copy() + arg2scope = dict(self._arg2scope) + for arg, val in zip(argnames, valset): + if arg in params: + raise nodes.Collector.CollectError( + f"{nodeid}: duplicate parametrization of {arg!r}" + ) + params[arg] = val + indices[arg] = param_index + arg2scope[arg] = scope + return CallSpec2( + params=params, + indices=indices, + _arg2scope=arg2scope, + _idlist=self._idlist if id is HIDDEN_PARAM else [*self._idlist, id], + marks=[*self.marks, *normalize_mark_list(marks)], + ) + + def getparam(self, name: str) -> object: + try: + return self.params[name] + except KeyError as e: + raise ValueError(name) from e + + @property + def id(self) -> str: + return "-".join(self._idlist) + + +def get_direct_param_fixture_func(request: FixtureRequest) -> Any: + return request.param + + +# Used for storing pseudo fixturedefs for direct parametrization. +name2pseudofixturedef_key = StashKey[dict[str, FixtureDef[Any]]]() + + +@final +class Metafunc: + """Objects passed to the :hook:`pytest_generate_tests` hook. + + They help to inspect a test function and to generate tests according to + test configuration or values specified in the class or module where a + test function is defined. + """ + + def __init__( + self, + definition: FunctionDefinition, + fixtureinfo: fixtures.FuncFixtureInfo, + config: Config, + cls=None, + module=None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + + #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. + self.definition = definition + + #: Access to the :class:`pytest.Config` object for the test session. + self.config = config + + #: The module object where the test function is defined in. + self.module = module + + #: Underlying Python test function. + self.function = definition.obj + + #: Set of fixture names required by the test function. + self.fixturenames = fixtureinfo.names_closure + + #: Class object where the test function is defined in or ``None``. + self.cls = cls + + self._arg2fixturedefs = fixtureinfo.name2fixturedefs + + # Result of parametrize(). + self._calls: list[CallSpec2] = [] + + self._params_directness: dict[str, Literal["indirect", "direct"]] = {} + + def parametrize( + self, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + indirect: bool | Sequence[str] = False, + ids: Iterable[object | None] | Callable[[Any], object | None] | None = None, + scope: _ScopeName | None = None, + *, + _param_mark: Mark | None = None, + ) -> None: + """Add new invocations to the underlying test function using the list + of argvalues for the given argnames. Parametrization is performed + during the collection phase. If you need to setup expensive resources + see about setting ``indirect`` to do it at test setup time instead. + + Can be called multiple times per test function (but only on different + argument names), in which case each call parametrizes all previous + parametrizations, e.g. + + :: + + unparametrized: t + parametrize ["x", "y"]: t[x], t[y] + parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2] + + :param argnames: + A comma-separated string denoting one or more argument names, or + a list/tuple of argument strings. + + :param argvalues: + The list of argvalues determines how often a test is invoked with + different argument values. + + If only one argname was specified argvalues is a list of values. + If N argnames were specified, argvalues must be a list of + N-tuples, where each tuple-element specifies a value for its + respective argname. + + :param indirect: + A list of arguments' names (subset of argnames) or a boolean. + If True the list contains all names from the argnames. Each + argvalue corresponding to an argname in this list will + be passed as request.param to its respective argname fixture + function so that it can perform more expensive setups during the + setup phase of a test rather than at collection time. + + :param ids: + Sequence of (or generator for) ids for ``argvalues``, + or a callable to return part of the id for each argvalue. + + With sequences (and generators like ``itertools.count()``) the + returned ids should be of type ``string``, ``int``, ``float``, + ``bool``, or ``None``. + They are mapped to the corresponding index in ``argvalues``. + ``None`` means to use the auto-generated id. + + .. versionadded:: 8.4 + :ref:`hidden-param` means to hide the parameter set + from the test name. Can only be used at most 1 time, as + test names need to be unique. + + If it is a callable it will be called for each entry in + ``argvalues``, and the return value is used as part of the + auto-generated id for the whole set (where parts are joined with + dashes ("-")). + This is useful to provide more specific ids for certain items, e.g. + dates. Returning ``None`` will use an auto-generated id. + + If no ids are provided they will be generated automatically from + the argvalues. + + :param scope: + If specified it denotes the scope of the parameters. + The scope is used for grouping tests by parameter instances. + It will also override any fixture-function defined scope, allowing + to set a dynamic scope using test context or configuration. + """ + nodeid = self.definition.nodeid + + argnames, parametersets = ParameterSet._for_parametrize( + argnames, + argvalues, + self.function, + self.config, + nodeid=self.definition.nodeid, + ) + del argvalues + + if "request" in argnames: + fail( + f"{nodeid}: 'request' is a reserved name and cannot be used in @pytest.mark.parametrize", + pytrace=False, + ) + + if scope is not None: + scope_ = Scope.from_user( + scope, descr=f"parametrize() call in {self.function.__name__}" + ) + else: + scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + + self._validate_if_using_arg_names(argnames, indirect) + + # Use any already (possibly) generated ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from: + generated_ids = _param_mark._param_ids_from._param_ids_generated + if generated_ids is not None: + ids = generated_ids + + ids = self._resolve_parameter_set_ids( + argnames, ids, parametersets, nodeid=self.definition.nodeid + ) + + # Store used (possibly generated) ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from and generated_ids is None: + object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) + + # Calculate directness. + arg_directness = self._resolve_args_directness(argnames, indirect) + self._params_directness.update(arg_directness) + + # Add direct parametrizations as fixturedefs to arg2fixturedefs by + # registering artificial "pseudo" FixtureDef's such that later at test + # setup time we can rely on FixtureDefs to exist for all argnames. + node = None + # For scopes higher than function, a "pseudo" FixtureDef might have + # already been created for the scope. We thus store and cache the + # FixtureDef on the node related to the scope. + if scope_ is Scope.Function: + name2pseudofixturedef = None + else: + collector = self.definition.parent + assert collector is not None + node = get_scope_node(collector, scope_) + if node is None: + # If used class scope and there is no class, use module-level + # collector (for now). + if scope_ is Scope.Class: + assert isinstance(collector, Module) + node = collector + # If used package scope and there is no package, use session + # (for now). + elif scope_ is Scope.Package: + node = collector.session + else: + assert False, f"Unhandled missing scope: {scope}" + default: dict[str, FixtureDef[Any]] = {} + name2pseudofixturedef = node.stash.setdefault( + name2pseudofixturedef_key, default + ) + for argname in argnames: + if arg_directness[argname] == "indirect": + continue + if name2pseudofixturedef is not None and argname in name2pseudofixturedef: + fixturedef = name2pseudofixturedef[argname] + else: + fixturedef = FixtureDef( + config=self.config, + baseid="", + argname=argname, + func=get_direct_param_fixture_func, + scope=scope_, + params=None, + ids=None, + _ispytest=True, + ) + if name2pseudofixturedef is not None: + name2pseudofixturedef[argname] = fixturedef + self._arg2fixturedefs[argname] = [fixturedef] + + # Create the new calls: if we are parametrize() multiple times (by applying the decorator + # more than once) then we accumulate those calls generating the cartesian product + # of all calls. + newcalls = [] + for callspec in self._calls or [CallSpec2()]: + for param_index, (param_id, param_set) in enumerate( + zip(ids, parametersets) + ): + newcallspec = callspec.setmulti( + argnames=argnames, + valset=param_set.values, + id=param_id, + marks=param_set.marks, + scope=scope_, + param_index=param_index, + nodeid=nodeid, + ) + newcalls.append(newcallspec) + self._calls = newcalls + + def _resolve_parameter_set_ids( + self, + argnames: Sequence[str], + ids: Iterable[object | None] | Callable[[Any], object | None] | None, + parametersets: Sequence[ParameterSet], + nodeid: str, + ) -> list[str | _HiddenParam]: + """Resolve the actual ids for the given parameter sets. + + :param argnames: + Argument names passed to ``parametrize()``. + :param ids: + The `ids` parameter of the ``parametrize()`` call (see docs). + :param parametersets: + The parameter sets, each containing a set of values corresponding + to ``argnames``. + :param nodeid str: + The nodeid of the definition item that generated this + parametrization. + :returns: + List with ids for each parameter set given. + """ + if ids is None: + idfn = None + ids_ = None + elif callable(ids): + idfn = ids + ids_ = None + else: + idfn = None + ids_ = self._validate_ids(ids, parametersets, self.function.__name__) + id_maker = IdMaker( + argnames, + parametersets, + idfn, + ids_, + self.config, + nodeid=nodeid, + func_name=self.function.__name__, + ) + return id_maker.make_unique_parameterset_ids() + + def _validate_ids( + self, + ids: Iterable[object | None], + parametersets: Sequence[ParameterSet], + func_name: str, + ) -> list[object | None]: + try: + num_ids = len(ids) # type: ignore[arg-type] + except TypeError: + try: + iter(ids) + except TypeError as e: + raise TypeError("ids must be a callable or an iterable") from e + num_ids = len(parametersets) + + # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 + if num_ids != len(parametersets) and num_ids != 0: + msg = "In {}: {} parameter sets specified, with different number of ids: {}" + fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False) + + return list(itertools.islice(ids, num_ids)) + + def _resolve_args_directness( + self, + argnames: Sequence[str], + indirect: bool | Sequence[str], + ) -> dict[str, Literal["indirect", "direct"]]: + """Resolve if each parametrized argument must be considered an indirect + parameter to a fixture of the same name, or a direct parameter to the + parametrized function, based on the ``indirect`` parameter of the + parametrized() call. + + :param argnames: + List of argument names passed to ``parametrize()``. + :param indirect: + Same as the ``indirect`` parameter of ``parametrize()``. + :returns + A dict mapping each arg name to either "indirect" or "direct". + """ + arg_directness: dict[str, Literal["indirect", "direct"]] + if isinstance(indirect, bool): + arg_directness = dict.fromkeys( + argnames, "indirect" if indirect else "direct" + ) + elif isinstance(indirect, Sequence): + arg_directness = dict.fromkeys(argnames, "direct") + for arg in indirect: + if arg not in argnames: + fail( + f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", + pytrace=False, + ) + arg_directness[arg] = "indirect" + else: + fail( + f"In {self.function.__name__}: expected Sequence or boolean" + f" for indirect, got {type(indirect).__name__}", + pytrace=False, + ) + return arg_directness + + def _validate_if_using_arg_names( + self, + argnames: Sequence[str], + indirect: bool | Sequence[str], + ) -> None: + """Check if all argnames are being used, by default values, or directly/indirectly. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. + :raises ValueError: If validation fails. + """ + default_arg_names = set(get_default_arg_names(self.function)) + func_name = self.function.__name__ + for arg in argnames: + if arg not in self.fixturenames: + if arg in default_arg_names: + fail( + f"In {func_name}: function already takes an argument '{arg}' with a default value", + pytrace=False, + ) + else: + if isinstance(indirect, Sequence): + name = "fixture" if arg in indirect else "argument" + else: + name = "fixture" if indirect else "argument" + fail( + f"In {func_name}: function uses no {name} '{arg}'", + pytrace=False, + ) + + def _recompute_direct_params_indices(self) -> None: + for argname, param_type in self._params_directness.items(): + if param_type == "direct": + for i, callspec in enumerate(self._calls): + callspec.indices[argname] = i + + +def _find_parametrized_scope( + argnames: Sequence[str], + arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], + indirect: bool | Sequence[str], +) -> Scope: + """Find the most appropriate scope for a parametrized call based on its arguments. + + When there's at least one direct argument, always use "function" scope. + + When a test function is parametrized and all its arguments are indirect + (e.g. fixtures), return the most narrow scope based on the fixtures used. + + Related to issue #1832, based on code posted by @Kingdread. + """ + if isinstance(indirect, Sequence): + all_arguments_are_fixtures = len(indirect) == len(argnames) + else: + all_arguments_are_fixtures = bool(indirect) + + if all_arguments_are_fixtures: + fixturedefs = arg2fixturedefs or {} + used_scopes = [ + fixturedef[-1]._scope + for name, fixturedef in fixturedefs.items() + if name in argnames + ] + # Takes the most narrow scope from used fixtures. + return min(used_scopes, default=Scope.Function) + + return Scope.Function + + +def _ascii_escaped_by_config(val: str | bytes, config: Config | None) -> str: + if config is None: + escape_option = False + else: + escape_option = config.getini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" + ) + # TODO: If escaping is turned off and the user passes bytes, + # will return a bytes. For now we ignore this but the + # code *probably* doesn't handle this case. + return val if escape_option else ascii_escaped(val) # type: ignore + + +class Function(PyobjMixin, nodes.Item): + """Item responsible for setting up and executing a Python test function. + + :param name: + The full function name, including any decorations like those + added by parametrization (``my_func[my_param]``). + :param parent: + The parent Node. + :param config: + The pytest Config object. + :param callspec: + If given, this function has been parametrized and the callspec contains + meta information about the parametrization. + :param callobj: + If given, the object which will be called when the Function is invoked, + otherwise the callobj will be obtained from ``parent`` using ``originalname``. + :param keywords: + Keywords bound to the function object for "-k" matching. + :param session: + The pytest Session object. + :param fixtureinfo: + Fixture information already resolved at this fixture node.. + :param originalname: + The attribute name to use for accessing the underlying function object. + Defaults to ``name``. Set this if name is different from the original name, + for example when it contains decorations like those added by parametrization + (``my_func[my_param]``). + """ + + # Disable since functions handle it themselves. + _ALLOW_MARKERS = False + + def __init__( + self, + name: str, + parent, + config: Config | None = None, + callspec: CallSpec2 | None = None, + callobj=NOTSET, + keywords: Mapping[str, Any] | None = None, + session: Session | None = None, + fixtureinfo: FuncFixtureInfo | None = None, + originalname: str | None = None, + ) -> None: + super().__init__(name, parent, config=config, session=session) + + if callobj is not NOTSET: + self._obj = callobj + self._instance = getattr(callobj, "__self__", None) + + #: Original function name, without any decorations (for example + #: parametrization adds a ``"[...]"`` suffix to function names), used to access + #: the underlying function object from ``parent`` (in case ``callobj`` is not given + #: explicitly). + #: + #: .. versionadded:: 3.0 + self.originalname = originalname or name + + # Note: when FunctionDefinition is introduced, we should change ``originalname`` + # to a readonly property that returns FunctionDefinition.name. + + self.own_markers.extend(get_unpacked_marks(self.obj)) + if callspec: + self.callspec = callspec + self.own_markers.extend(callspec.marks) + + # todo: this is a hell of a hack + # https://github.com/pytest-dev/pytest/issues/4569 + # Note: the order of the updates is important here; indicates what + # takes priority (ctor argument over function attributes over markers). + # Take own_markers only; NodeKeywords handles parent traversal on its own. + self.keywords.update((mark.name, mark) for mark in self.own_markers) + self.keywords.update(self.obj.__dict__) + if keywords: + self.keywords.update(keywords) + + if fixtureinfo is None: + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) + self._fixtureinfo: FuncFixtureInfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + # todo: determine sound type limitations + @classmethod + def from_parent(cls, parent, **kw) -> Self: + """The public constructor.""" + return super().from_parent(parent=parent, **kw) + + def _initrequest(self) -> None: + self.funcargs: dict[str, object] = {} + self._request = fixtures.TopRequest(self, _ispytest=True) + + @property + def function(self): + """Underlying python 'function' object.""" + return getimfunc(self.obj) + + @property + def instance(self): + try: + return self._instance + except AttributeError: + if isinstance(self.parent, Class): + # Each Function gets a fresh class instance. + self._instance = self._getinstance() + else: + self._instance = None + return self._instance + + def _getinstance(self): + if isinstance(self.parent, Class): + # Each Function gets a fresh class instance. + return self.parent.newinstance() + else: + return None + + def _getobj(self): + instance = self.instance + if instance is not None: + parent_obj = instance + else: + assert self.parent is not None + parent_obj = self.parent.obj # type: ignore[attr-defined] + return getattr(parent_obj, self.originalname) + + @property + def _pyfuncitem(self): + """(compatonly) for code expecting pytest-2.2 style request objects.""" + return self + + def runtest(self) -> None: + """Execute the underlying test function.""" + self.ihook.pytest_pyfunc_call(pyfuncitem=self) + + def setup(self) -> None: + self._request._fillfixtures() + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): + code = _pytest._code.Code.from_function(get_real_func(self.obj)) + path, firstlineno = code.path, code.firstlineno + traceback = excinfo.traceback + ntraceback = traceback.cut(path=path, firstlineno=firstlineno) + if ntraceback == traceback: + ntraceback = ntraceback.cut(path=path) + if ntraceback == traceback: + ntraceback = ntraceback.filter(filter_traceback) + if not ntraceback: + ntraceback = traceback + ntraceback = ntraceback.filter(excinfo) + + # issue364: mark all but first and last frames to + # only show a single-line message for each frame. + if self.config.getoption("tbstyle", "auto") == "auto": + if len(ntraceback) > 2: + ntraceback = Traceback( + ( + ntraceback[0], + *(t.with_repr_style("short") for t in ntraceback[1:-1]), + ntraceback[-1], + ) + ) + + return ntraceback + return excinfo.traceback + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, + excinfo: ExceptionInfo[BaseException], + ) -> str | TerminalRepr: + style = self.config.getoption("tbstyle", "auto") + if style == "auto": + style = "long" + return self._repr_failure_py(excinfo, style=style) + + +class FunctionDefinition(Function): + """This class is a stop gap solution until we evolve to have actual function + definition nodes and manage to get rid of ``metafunc``.""" + + def runtest(self) -> None: + raise RuntimeError("function definitions are not supposed to be run as tests") + + setup = runtest diff --git a/.venv/lib/python3.11/site-packages/_pytest/python_api.py b/.venv/lib/python3.11/site-packages/_pytest/python_api.py new file mode 100644 index 00000000..74346c34 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/python_api.py @@ -0,0 +1,809 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Collection +from collections.abc import Mapping +from collections.abc import Sequence +from collections.abc import Sized +from decimal import Decimal +import math +from numbers import Complex +import pprint +import sys +from typing import Any +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from numpy import ndarray + + +def _compare_approx( + full_object: object, + message_data: Sequence[tuple[str, str, str]], + number_of_elements: int, + different_ids: Sequence[object], + max_abs_diff: float, + max_rel_diff: float, +) -> list[str]: + message_list = list(message_data) + message_list.insert(0, ("Index", "Obtained", "Expected")) + max_sizes = [0, 0, 0] + for index, obtained, expected in message_list: + max_sizes[0] = max(max_sizes[0], len(index)) + max_sizes[1] = max(max_sizes[1], len(obtained)) + max_sizes[2] = max(max_sizes[2], len(expected)) + explanation = [ + f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", + f"Max absolute difference: {max_abs_diff}", + f"Max relative difference: {max_rel_diff}", + ] + [ + f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" + for indexes, obtained, expected in message_list + ] + return explanation + + +# builtin pytest.approx helper + + +class ApproxBase: + """Provide shared utilities for making approximate comparisons between + numbers or sequences of numbers.""" + + # Tell numpy to use our `__eq__` operator instead of its. + __array_ufunc__ = None + __array_priority__ = 100 + + def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: + __tracebackhide__ = True + self.expected = expected + self.abs = abs + self.rel = rel + self.nan_ok = nan_ok + self._check_type() + + def __repr__(self) -> str: + raise NotImplementedError + + def _repr_compare(self, other_side: Any) -> list[str]: + return [ + "comparison failed", + f"Obtained: {other_side}", + f"Expected: {self}", + ] + + def __eq__(self, actual) -> bool: + return all( + a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) + ) + + def __bool__(self): + __tracebackhide__ = True + raise AssertionError( + "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" + ) + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + def __ne__(self, actual) -> bool: + return not (actual == self) + + def _approx_scalar(self, x) -> ApproxScalar: + if isinstance(x, Decimal): + return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + + def _yield_comparisons(self, actual): + """Yield all the pairs of numbers to be compared. + + This is used to implement the `__eq__` method. + """ + raise NotImplementedError + + def _check_type(self) -> None: + """Raise a TypeError if the expected value is not a valid type.""" + # This is only a concern if the expected value is a sequence. In every + # other case, the approx() function ensures that the expected value has + # a numeric type. For this reason, the default is to do nothing. The + # classes that deal with sequences should reimplement this method to + # raise if there are any non-numeric elements in the sequence. + + +def _recursive_sequence_map(f, x): + """Recursively map a function over a sequence of arbitrary depth""" + if isinstance(x, (list, tuple)): + seq_type = type(x) + return seq_type(_recursive_sequence_map(f, xi) for xi in x) + elif _is_sequence_like(x): + return [_recursive_sequence_map(f, xi) for xi in x] + else: + return f(x) + + +class ApproxNumpy(ApproxBase): + """Perform approximate comparisons where the expected value is numpy array.""" + + def __repr__(self) -> str: + list_scalars = _recursive_sequence_map( + self._approx_scalar, self.expected.tolist() + ) + return f"approx({list_scalars!r})" + + def _repr_compare(self, other_side: ndarray | list[Any]) -> list[str]: + import itertools + import math + + def get_value_from_nested_list( + nested_list: list[Any], nd_index: tuple[Any, ...] + ) -> Any: + """ + Helper function to get the value out of a nested list, given an n-dimensional index. + This mimics numpy's indexing, but for raw nested python lists. + """ + value: Any = nested_list + for i in nd_index: + value = value[i] + return value + + np_array_shape = self.expected.shape + approx_side_as_seq = _recursive_sequence_map( + self._approx_scalar, self.expected.tolist() + ) + + # convert other_side to numpy array to ensure shape attribute is available + other_side_as_array = _as_numpy_array(other_side) + assert other_side_as_array is not None + + if np_array_shape != other_side_as_array.shape: + return [ + "Impossible to compare arrays with different shapes.", + f"Shapes: {np_array_shape} and {other_side_as_array.shape}", + ] + + number_of_elements = self.expected.size + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for index in itertools.product(*(range(i) for i in np_array_shape)): + approx_value = get_value_from_nested_list(approx_side_as_seq, index) + other_value = get_value_from_nested_list(other_side_as_array, index) + if approx_value != other_value: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + if other_value == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(index) + + message_data = [ + ( + str(index), + str(get_value_from_nested_list(other_side_as_array, index)), + str(get_value_from_nested_list(approx_side_as_seq, index)), + ) + for index in different_ids + ] + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + import numpy as np + + # self.expected is supposed to always be an array here. + + if not np.isscalar(actual): + try: + actual = np.asarray(actual) + except Exception as e: + raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e + + if not np.isscalar(actual) and actual.shape != self.expected.shape: + return False + + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + import numpy as np + + # `actual` can either be a numpy array or a scalar, it is treated in + # `__eq__` before being passed to `ApproxBase.__eq__`, which is the + # only method that calls this one. + + if np.isscalar(actual): + for i in np.ndindex(self.expected.shape): + yield actual, self.expected[i].item() + else: + for i in np.ndindex(self.expected.shape): + yield actual[i].item(), self.expected[i].item() + + +class ApproxMapping(ApproxBase): + """Perform approximate comparisons where the expected value is a mapping + with numeric values (the keys can be anything).""" + + def __repr__(self) -> str: + return f"approx({ ({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})" + + def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: + import math + + approx_side_as_map = { + k: self._approx_scalar(v) for k, v in self.expected.items() + } + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for (approx_key, approx_value), other_value in zip( + approx_side_as_map.items(), other_side.values() + ): + if approx_value != other_value: + if approx_value.expected is not None and other_value is not None: + try: + max_abs_diff = max( + max_abs_diff, abs(approx_value.expected - other_value) + ) + if approx_value.expected == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max( + max_rel_diff, + abs( + (approx_value.expected - other_value) + / approx_value.expected + ), + ) + except ZeroDivisionError: + pass + different_ids.append(approx_key) + + message_data = [ + (str(key), str(other_side[key]), str(approx_side_as_map[key])) + for key in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + try: + if set(actual.keys()) != set(self.expected.keys()): + return False + except AttributeError: + return False + + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + for k in self.expected.keys(): + yield actual[k], self.expected[k] + + def _check_type(self) -> None: + __tracebackhide__ = True + for key, value in self.expected.items(): + if isinstance(value, type(self.expected)): + msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" + raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) + + +class ApproxSequenceLike(ApproxBase): + """Perform approximate comparisons where the expected value is a sequence of numbers.""" + + def __repr__(self) -> str: + seq_type = type(self.expected) + if seq_type not in (tuple, list): + seq_type = list + return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})" + + def _repr_compare(self, other_side: Sequence[float]) -> list[str]: + import math + + if len(self.expected) != len(other_side): + return [ + "Impossible to compare lists with different sizes.", + f"Lengths: {len(self.expected)} and {len(other_side)}", + ] + + approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected) + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for i, (approx_value, other_value) in enumerate( + zip(approx_side_as_map, other_side) + ): + if approx_value != other_value: + try: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + # Ignore non-numbers for the diff calculations (#13012). + except TypeError: + pass + else: + if other_value == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(i) + message_data = [ + (str(i), str(other_side[i]), str(approx_side_as_map[i])) + for i in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + try: + if len(actual) != len(self.expected): + return False + except TypeError: + return False + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + return zip(actual, self.expected) + + def _check_type(self) -> None: + __tracebackhide__ = True + for index, x in enumerate(self.expected): + if isinstance(x, type(self.expected)): + msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" + raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) + + +class ApproxScalar(ApproxBase): + """Perform approximate comparisons where the expected value is a single number.""" + + # Using Real should be better than this Union, but not possible yet: + # https://github.com/python/typeshed/pull/3108 + DEFAULT_ABSOLUTE_TOLERANCE: float | Decimal = 1e-12 + DEFAULT_RELATIVE_TOLERANCE: float | Decimal = 1e-6 + + def __repr__(self) -> str: + """Return a string communicating both the expected value and the + tolerance for the comparison being made. + + For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. + """ + # Don't show a tolerance for values that aren't compared using + # tolerances, i.e. non-numerics and infinities. Need to call abs to + # handle complex numbers, e.g. (inf + 1j). + if ( + isinstance(self.expected, bool) + or (not isinstance(self.expected, (Complex, Decimal))) + or math.isinf(abs(self.expected) or isinstance(self.expected, bool)) + ): + return str(self.expected) + + # If a sensible tolerance can't be calculated, self.tolerance will + # raise a ValueError. In this case, display '???'. + try: + if 1e-3 <= self.tolerance < 1e3: + vetted_tolerance = f"{self.tolerance:n}" + else: + vetted_tolerance = f"{self.tolerance:.1e}" + + if ( + isinstance(self.expected, Complex) + and self.expected.imag + and not math.isinf(self.tolerance) + ): + vetted_tolerance += " ∠ ±180°" + except ValueError: + vetted_tolerance = "???" + + return f"{self.expected} ± {vetted_tolerance}" + + def __eq__(self, actual) -> bool: + """Return whether the given value is equal to the expected value + within the pre-specified tolerance.""" + + def is_bool(val: Any) -> bool: + # Check if `val` is a native bool or numpy bool. + if isinstance(val, bool): + return True + if np := sys.modules.get("numpy"): + return isinstance(val, np.bool_) + return False + + asarray = _as_numpy_array(actual) + if asarray is not None: + # Call ``__eq__()`` manually to prevent infinite-recursion with + # numpy<1.13. See #3748. + return all(self.__eq__(a) for a in asarray.flat) + + # Short-circuit exact equality, except for bool and np.bool_ + if is_bool(self.expected) and not is_bool(actual): + return False + elif actual == self.expected: + return True + + # If either type is non-numeric, fall back to strict equality. + # NB: we need Complex, rather than just Number, to ensure that __abs__, + # __sub__, and __float__ are defined. Also, consider bool to be + # non-numeric, even though it has the required arithmetic. + if is_bool(self.expected) or not ( + isinstance(self.expected, (Complex, Decimal)) + and isinstance(actual, (Complex, Decimal)) + ): + return False + + # Allow the user to control whether NaNs are considered equal to each + # other or not. The abs() calls are for compatibility with complex + # numbers. + if math.isnan(abs(self.expected)): + return self.nan_ok and math.isnan(abs(actual)) + + # Infinity shouldn't be approximately equal to anything but itself, but + # if there's a relative tolerance, it will be infinite and infinity + # will seem approximately equal to everything. The equal-to-itself + # case would have been short circuited above, so here we can just + # return false if the expected value is infinite. The abs() call is + # for compatibility with complex numbers. + if math.isinf(abs(self.expected)): + return False + + # Return true if the two numbers are within the tolerance. + result: bool = abs(self.expected - actual) <= self.tolerance + return result + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def tolerance(self): + """Return the tolerance for the comparison. + + This could be either an absolute tolerance or a relative tolerance, + depending on what the user specified or which would be larger. + """ + + def set_default(x, default): + return x if x is not None else default + + # Figure out what the absolute tolerance should be. ``self.abs`` is + # either None or a value specified by the user. + absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) + + if absolute_tolerance < 0: + raise ValueError( + f"absolute tolerance can't be negative: {absolute_tolerance}" + ) + if math.isnan(absolute_tolerance): + raise ValueError("absolute tolerance can't be NaN.") + + # If the user specified an absolute tolerance but not a relative one, + # just return the absolute tolerance. + if self.rel is None: + if self.abs is not None: + return absolute_tolerance + + # Figure out what the relative tolerance should be. ``self.rel`` is + # either None or a value specified by the user. This is done after + # we've made sure the user didn't ask for an absolute tolerance only, + # because we don't want to raise errors about the relative tolerance if + # we aren't even going to use it. + relative_tolerance = set_default( + self.rel, self.DEFAULT_RELATIVE_TOLERANCE + ) * abs(self.expected) + + if relative_tolerance < 0: + raise ValueError( + f"relative tolerance can't be negative: {relative_tolerance}" + ) + if math.isnan(relative_tolerance): + raise ValueError("relative tolerance can't be NaN.") + + # Return the larger of the relative and absolute tolerances. + return max(relative_tolerance, absolute_tolerance) + + +class ApproxDecimal(ApproxScalar): + """Perform approximate comparisons where the expected value is a Decimal.""" + + DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") + DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") + + def __repr__(self) -> str: + if isinstance(self.rel, float): + rel = Decimal.from_float(self.rel) + else: + rel = self.rel + + if isinstance(self.abs, float): + abs_ = Decimal.from_float(self.abs) + else: + abs_ = self.abs + + tol_str = "???" + if rel is not None and Decimal("1e-3") <= rel <= Decimal("1e3"): + tol_str = f"{rel:.1e}" + elif abs_ is not None: + tol_str = f"{abs_:.1e}" + + return f"{self.expected} ± {tol_str}" + + +def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: + """Assert that two numbers (or two ordered sequences of numbers) are equal to each other + within some tolerance. + + Due to the :doc:`python:tutorial/floatingpoint`, numbers that we + would intuitively expect to be equal are not always so:: + + >>> 0.1 + 0.2 == 0.3 + False + + This problem is commonly encountered when writing tests, e.g. when making + sure that floating-point values are what you expect them to be. One way to + deal with this problem is to assert that two floating-point numbers are + equal to within some appropriate tolerance:: + + >>> abs((0.1 + 0.2) - 0.3) < 1e-6 + True + + However, comparisons like this are tedious to write and difficult to + understand. Furthermore, absolute comparisons like the one above are + usually discouraged because there's no tolerance that works well for all + situations. ``1e-6`` is good for numbers around ``1``, but too small for + very big numbers and too big for very small ones. It's better to express + the tolerance as a fraction of the expected value, but relative comparisons + like that are even more difficult to write correctly and concisely. + + The ``approx`` class performs floating-point comparisons using a syntax + that's as intuitive as possible:: + + >>> from pytest import approx + >>> 0.1 + 0.2 == approx(0.3) + True + + The same syntax also works for ordered sequences of numbers:: + + >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) + True + + ``numpy`` arrays:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP + True + + And for a ``numpy`` array against a scalar:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP + True + + Only ordered sequences are supported, because ``approx`` needs + to infer the relative position of the sequences without ambiguity. This means + ``sets`` and other unordered sequences are not supported. + + Finally, dictionary *values* can also be compared:: + + >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) + True + + The comparison will be true if both mappings have the same keys and their + respective values match the expected tolerances. + + **Tolerances** + + By default, ``approx`` considers numbers within a relative tolerance of + ``1e-6`` (i.e. one part in a million) of its expected value to be equal. + This treatment would lead to surprising results if the expected value was + ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. + To handle this case less surprisingly, ``approx`` also considers numbers + within an absolute tolerance of ``1e-12`` of its expected value to be + equal. Infinity and NaN are special cases. Infinity is only considered + equal to itself, regardless of the relative tolerance. NaN is not + considered equal to anything by default, but you can make it be equal to + itself by setting the ``nan_ok`` argument to True. (This is meant to + facilitate comparing arrays that use NaN to mean "no data".) + + Both the relative and absolute tolerances can be changed by passing + arguments to the ``approx`` constructor:: + + >>> 1.0001 == approx(1) + False + >>> 1.0001 == approx(1, rel=1e-3) + True + >>> 1.0001 == approx(1, abs=1e-3) + True + + If you specify ``abs`` but not ``rel``, the comparison will not consider + the relative tolerance at all. In other words, two numbers that are within + the default relative tolerance of ``1e-6`` will still be considered unequal + if they exceed the specified absolute tolerance. If you specify both + ``abs`` and ``rel``, the numbers will be considered equal if either + tolerance is met:: + + >>> 1 + 1e-8 == approx(1) + True + >>> 1 + 1e-8 == approx(1, abs=1e-12) + False + >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) + True + + **Non-numeric types** + + You can also use ``approx`` to compare non-numeric types, or dicts and + sequences containing non-numeric types, in which case it falls back to + strict equality. This can be useful for comparing dicts and sequences that + can contain optional values:: + + >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) + True + >>> [None, 1.0000005] == approx([None,1]) + True + >>> ["foo", 1.0000005] == approx([None,1]) + False + + If you're thinking about using ``approx``, then you might want to know how + it compares to other good ways of comparing floating-point numbers. All of + these algorithms are based on relative and absolute tolerances and should + agree for the most part, but they do have meaningful differences: + + - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative + tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute + tolerance is met. Because the relative tolerance is calculated w.r.t. + both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor + ``b`` is a "reference value"). You have to specify an absolute tolerance + if you want to compare to ``0.0`` because there is no tolerance by + default. More information: :py:func:`math.isclose`. + + - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference + between ``a`` and ``b`` is less that the sum of the relative tolerance + w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance + is only calculated w.r.t. ``b``, this test is asymmetric and you can + think of ``b`` as the reference value. Support for comparing sequences + is provided by :py:func:`numpy.allclose`. More information: + :std:doc:`numpy:reference/generated/numpy.isclose`. + + - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` + are within an absolute tolerance of ``1e-7``. No relative tolerance is + considered , so this function is not appropriate for very large or very + small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` + and it's ugly because it doesn't follow PEP8. More information: + :py:meth:`unittest.TestCase.assertAlmostEqual`. + + - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative + tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. + Because the relative tolerance is only calculated w.r.t. ``b``, this test + is asymmetric and you can think of ``b`` as the reference value. In the + special case that you explicitly specify an absolute tolerance but not a + relative tolerance, only the absolute tolerance is considered. + + .. note:: + + ``approx`` can handle numpy arrays, but we recommend the + specialised test helpers in :std:doc:`numpy:reference/routines.testing` + if you need support for comparisons, NaNs, or ULP-based tolerances. + + To match strings using regex, you can use + `Matches `_ + from the + `re_assert package `_. + + + .. note:: + + Unlike built-in equality, this function considers + booleans unequal to numeric zero or one. For example:: + + >>> 1 == approx(True) + False + + .. warning:: + + .. versionchanged:: 3.2 + + In order to avoid inconsistent behavior, :py:exc:`TypeError` is + raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. + The example below illustrates the problem:: + + assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) + assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) + + In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` + to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to + comparison. This is because the call hierarchy of rich comparisons + follows a fixed behavior. More information: :py:meth:`object.__ge__` + + .. versionchanged:: 3.7.1 + ``approx`` raises ``TypeError`` when it encounters a dict value or + sequence element of non-numeric type. + + .. versionchanged:: 6.1.0 + ``approx`` falls back to strict equality for non-numeric types instead + of raising ``TypeError``. + """ + # Delegate the comparison to a class that knows how to deal with the type + # of the expected value (e.g. int, float, list, dict, numpy.array, etc). + # + # The primary responsibility of these classes is to implement ``__eq__()`` + # and ``__repr__()``. The former is used to actually check if some + # "actual" value is equivalent to the given expected value within the + # allowed tolerance. The latter is used to show the user the expected + # value and tolerance, in the case that a test failed. + # + # The actual logic for making approximate comparisons can be found in + # ApproxScalar, which is used to compare individual numbers. All of the + # other Approx classes eventually delegate to this class. The ApproxBase + # class provides some convenient methods and overloads, but isn't really + # essential. + + __tracebackhide__ = True + + if isinstance(expected, Decimal): + cls: type[ApproxBase] = ApproxDecimal + elif isinstance(expected, Mapping): + cls = ApproxMapping + elif _is_numpy_array(expected): + expected = _as_numpy_array(expected) + cls = ApproxNumpy + elif _is_sequence_like(expected): + cls = ApproxSequenceLike + elif isinstance(expected, Collection) and not isinstance(expected, (str, bytes)): + msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}" + raise TypeError(msg) + else: + cls = ApproxScalar + + return cls(expected, rel, abs, nan_ok) + + +def _is_sequence_like(expected: object) -> bool: + return ( + hasattr(expected, "__getitem__") + and isinstance(expected, Sized) + and not isinstance(expected, (str, bytes)) + ) + + +def _is_numpy_array(obj: object) -> bool: + """ + Return true if the given object is implicitly convertible to ndarray, + and numpy is already imported. + """ + return _as_numpy_array(obj) is not None + + +def _as_numpy_array(obj: object) -> ndarray | None: + """ + Return an ndarray if the given object is implicitly convertible to ndarray, + and numpy is already imported, otherwise None. + """ + np: Any = sys.modules.get("numpy") + if np is not None: + # avoid infinite recursion on numpy scalars, which have __array__ + if np.isscalar(obj): + return None + elif isinstance(obj, np.ndarray): + return obj + elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + return np.asarray(obj) + return None diff --git a/.venv/lib/python3.11/site-packages/_pytest/raises.py b/.venv/lib/python3.11/site-packages/_pytest/raises.py new file mode 100644 index 00000000..78fae6dd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/raises.py @@ -0,0 +1,1519 @@ +from __future__ import annotations + +from abc import ABC +from abc import abstractmethod +import re +from re import Pattern +import sys +from textwrap import indent +from typing import Any +from typing import cast +from typing import final +from typing import Generic +from typing import get_args +from typing import get_origin +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import warnings + +from _pytest._code import ExceptionInfo +from _pytest._code.code import stringify_exception +from _pytest.outcomes import fail +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from collections.abc import Callable + from collections.abc import Sequence + + # for some reason Sphinx does not play well with 'from types import TracebackType' + import types + + from typing_extensions import ParamSpec + from typing_extensions import TypeGuard + from typing_extensions import TypeVar + + P = ParamSpec("P") + + # this conditional definition is because we want to allow a TypeVar default + BaseExcT_co_default = TypeVar( + "BaseExcT_co_default", + bound=BaseException, + default=BaseException, + covariant=True, + ) + + # Use short name because it shows up in docs. + E = TypeVar("E", bound=BaseException, default=BaseException) +else: + from typing import TypeVar + + BaseExcT_co_default = TypeVar( + "BaseExcT_co_default", bound=BaseException, covariant=True + ) + +# RaisesGroup doesn't work with a default. +BaseExcT_co = TypeVar("BaseExcT_co", bound=BaseException, covariant=True) +BaseExcT_1 = TypeVar("BaseExcT_1", bound=BaseException) +BaseExcT_2 = TypeVar("BaseExcT_2", bound=BaseException) +ExcT_1 = TypeVar("ExcT_1", bound=Exception) +ExcT_2 = TypeVar("ExcT_2", bound=Exception) + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + from exceptiongroup import ExceptionGroup + + +# String patterns default to including the unicode flag. +_REGEX_NO_FLAGS = re.compile(r"").flags + + +# pytest.raises helper +@overload +def raises( + expected_exception: type[E] | tuple[type[E], ...], + *, + match: str | re.Pattern[str] | None = ..., + check: Callable[[E], bool] = ..., +) -> RaisesExc[E]: ... + + +@overload +def raises( + *, + match: str | re.Pattern[str], + # If exception_type is not provided, check() must do any typechecks itself. + check: Callable[[BaseException], bool] = ..., +) -> RaisesExc[BaseException]: ... + + +@overload +def raises(*, check: Callable[[BaseException], bool]) -> RaisesExc[BaseException]: ... + + +@overload +def raises( + expected_exception: type[E] | tuple[type[E], ...], + func: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> ExceptionInfo[E]: ... + + +def raises( + expected_exception: type[E] | tuple[type[E], ...] | None = None, + *args: Any, + **kwargs: Any, +) -> RaisesExc[BaseException] | ExceptionInfo[E]: + r"""Assert that a code block/function call raises an exception type, or one of its subclasses. + + :param expected_exception: + The expected exception type, or a tuple if one of multiple possible + exception types are expected. Note that subclasses of the passed exceptions + will also match. + + This is not a required parameter, you may opt to only use ``match`` and/or + ``check`` for verifying the raised exception. + + :kwparam str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception and its :pep:`678` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + (This is only used when ``pytest.raises`` is used as a context manager, + and passed through to the function otherwise. + When using ``pytest.raises`` as a function, you can use: + ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) + + :kwparam Callable[[BaseException], bool] check: + + .. versionadded:: 8.4 + + If specified, a callable that will be called with the exception as a parameter + after checking the type and the match regex if specified. + If it returns ``True`` it will be considered a match, if not it will + be considered a failed match. + + + Use ``pytest.raises`` as a context manager, which will capture the exception of the given + type, or any of its subclasses:: + + >>> import pytest + >>> with pytest.raises(ZeroDivisionError): + ... 1/0 + + If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example + above), or no exception at all, the check will fail instead. + + You can also use the keyword argument ``match`` to assert that the + exception matches a text or regex:: + + >>> with pytest.raises(ValueError, match='must be 0 or None'): + ... raise ValueError("value must be 0 or None") + + >>> with pytest.raises(ValueError, match=r'must be \d+$'): + ... raise ValueError("value must be 42") + + The ``match`` argument searches the formatted exception string, which includes any + `PEP-678 `__ ``__notes__``: + + >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP + ... e = ValueError("value must be 42") + ... e.add_note("had a note added") + ... raise e + + The ``check`` argument, if provided, must return True when passed the raised exception + for the match to be successful, otherwise an :exc:`AssertionError` is raised. + + >>> import errno + >>> with pytest.raises(OSError, check=lambda e: e.errno == errno.EACCES): + ... raise OSError(errno.EACCES, "no permission to view") + + The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the + details of the captured exception:: + + >>> with pytest.raises(ValueError) as exc_info: + ... raise ValueError("value must be 42") + >>> assert exc_info.type is ValueError + >>> assert exc_info.value.args[0] == "value must be 42" + + .. warning:: + + Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: + + # Careful, this will catch ANY exception raised. + with pytest.raises(Exception): + some_function() + + Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide + real bugs, where the user wrote this expecting a specific exception, but some other exception is being + raised due to a bug introduced during a refactoring. + + Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch + **any** exception raised. + + .. note:: + + When using ``pytest.raises`` as a context manager, it's worthwhile to + note that normal context manager rules apply and that the exception + raised *must* be the final line in the scope of the context manager. + Lines of code after that, within the scope of the context manager will + not be executed. For example:: + + >>> value = 15 + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... assert exc_info.type is ValueError # This will not execute. + + Instead, the following approach must be taken (note the difference in + scope):: + + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... + >>> assert exc_info.type is ValueError + + **Expecting exception groups** + + When expecting exceptions wrapped in :exc:`BaseExceptionGroup` or + :exc:`ExceptionGroup`, you should instead use :class:`pytest.RaisesGroup`. + + **Using with** ``pytest.mark.parametrize`` + + When using :ref:`pytest.mark.parametrize ref` + it is possible to parametrize tests such that + some runs raise an exception and others do not. + + See :ref:`parametrizing_conditional_raising` for an example. + + .. seealso:: + + :ref:`assertraises` for more examples and detailed discussion. + + **Legacy form** + + It is possible to specify a callable by passing a to-be-called lambda:: + + >>> raises(ZeroDivisionError, lambda: 1/0) + + + or you can specify an arbitrary callable with arguments:: + + >>> def f(x): return 1/x + ... + >>> raises(ZeroDivisionError, f, 0) + + >>> raises(ZeroDivisionError, f, x=0) + + + The form above is fully supported but discouraged for new code because the + context manager form is regarded as more readable and less error-prone. + + .. note:: + Similar to caught exception objects in Python, explicitly clearing + local references to returned ``ExceptionInfo`` objects can + help the Python interpreter speed up its garbage collection. + + Clearing those references breaks a reference cycle + (``ExceptionInfo`` --> caught exception --> frame stack raising + the exception --> current frame stack --> local variables --> + ``ExceptionInfo``) which makes Python keep all objects referenced + from that cycle (including all local variables in the current + frame) alive until the next cyclic garbage collection run. + More detailed information can be found in the official Python + documentation for :ref:`the try statement `. + """ + __tracebackhide__ = True + + if not args: + if set(kwargs) - {"match", "check", "expected_exception"}: + msg = "Unexpected keyword arguments passed to pytest.raises: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + + if expected_exception is None: + return RaisesExc(**kwargs) + return RaisesExc(expected_exception, **kwargs) + + if not expected_exception: + raise ValueError( + f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " + f"Raising exceptions is already understood as failing the test, so you don't need " + f"any special code to say 'this should never raise an exception'." + ) + func = args[0] + if not callable(func): + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + with RaisesExc(expected_exception) as excinfo: + func(*args[1:], **kwargs) + try: + return excinfo + finally: + del excinfo + + +# note: RaisesExc/RaisesGroup uses fail() internally, so this alias +# indicates (to [internal] plugins?) that `pytest.raises` will +# raise `_pytest.outcomes.Failed`, where +# `outcomes.Failed is outcomes.fail.Exception is raises.Exception` +# note: this is *not* the same as `_pytest.main.Failed` +# note: mypy does not recognize this attribute, and it's not possible +# to use a protocol/decorator like the others in outcomes due to +# https://github.com/python/mypy/issues/18715 +raises.Exception = fail.Exception # type: ignore[attr-defined] + + +def _match_pattern(match: Pattern[str]) -> str | Pattern[str]: + """Helper function to remove redundant `re.compile` calls when printing regex""" + return match.pattern if match.flags == _REGEX_NO_FLAGS else match + + +def repr_callable(fun: Callable[[BaseExcT_1], bool]) -> str: + """Get the repr of a ``check`` parameter. + + Split out so it can be monkeypatched (e.g. by hypothesis) + """ + return repr(fun) + + +def backquote(s: str) -> str: + return "`" + s + "`" + + +def _exception_type_name( + e: type[BaseException] | tuple[type[BaseException], ...], +) -> str: + if isinstance(e, type): + return e.__name__ + if len(e) == 1: + return e[0].__name__ + return "(" + ", ".join(ee.__name__ for ee in e) + ")" + + +def _check_raw_type( + expected_type: type[BaseException] | tuple[type[BaseException], ...] | None, + exception: BaseException, +) -> str | None: + if expected_type is None or expected_type == (): + return None + + if not isinstance( + exception, + expected_type, + ): + actual_type_str = backquote(_exception_type_name(type(exception)) + "()") + expected_type_str = backquote(_exception_type_name(expected_type)) + if ( + isinstance(exception, BaseExceptionGroup) + and isinstance(expected_type, type) + and not issubclass(expected_type, BaseExceptionGroup) + ): + return f"Unexpected nested {actual_type_str}, expected {expected_type_str}" + return f"{actual_type_str} is not an instance of {expected_type_str}" + return None + + +def is_fully_escaped(s: str) -> bool: + # we know we won't compile with re.VERBOSE, so whitespace doesn't need to be escaped + metacharacters = "{}()+.*?^$[]" + return not any( + c in metacharacters and (i == 0 or s[i - 1] != "\\") for (i, c) in enumerate(s) + ) + + +def unescape(s: str) -> str: + return re.sub(r"\\([{}()+-.*?^$\[\]\s\\])", r"\1", s) + + +# These classes conceptually differ from ExceptionInfo in that ExceptionInfo is tied, and +# constructed from, a particular exception - whereas these are constructed with expected +# exceptions, and later allow matching towards particular exceptions. +# But there's overlap in `ExceptionInfo.match` and `AbstractRaises._check_match`, as with +# `AbstractRaises.matches` and `ExceptionInfo.errisinstance`+`ExceptionInfo.group_contains`. +# The interaction between these classes should perhaps be improved. +class AbstractRaises(ABC, Generic[BaseExcT_co]): + """ABC with common functionality shared between RaisesExc and RaisesGroup""" + + def __init__( + self, + *, + match: str | Pattern[str] | None, + check: Callable[[BaseExcT_co], bool] | None, + ) -> None: + if isinstance(match, str): + # juggle error in order to avoid context to fail (necessary?) + re_error = None + try: + self.match: Pattern[str] | None = re.compile(match) + except re.error as e: + re_error = e + if re_error is not None: + fail(f"Invalid regex pattern provided to 'match': {re_error}") + if match == "": + warnings.warn( + PytestWarning( + "matching against an empty string will *always* pass. If you want " + "to check for an empty message you need to pass '^$'. If you don't " + "want to match you should pass `None` or leave out the parameter." + ), + stacklevel=2, + ) + else: + self.match = match + + # check if this is a fully escaped regex and has ^$ to match fully + # in which case we can do a proper diff on error + self.rawmatch: str | None = None + if isinstance(match, str) or ( + isinstance(match, Pattern) and match.flags == _REGEX_NO_FLAGS + ): + if isinstance(match, Pattern): + match = match.pattern + if ( + match + and match[0] == "^" + and match[-1] == "$" + and is_fully_escaped(match[1:-1]) + ): + self.rawmatch = unescape(match[1:-1]) + + self.check = check + self._fail_reason: str | None = None + + # used to suppress repeated printing of `repr(self.check)` + self._nested: bool = False + + # set in self._parse_exc + self.is_baseexception = False + + def _parse_exc( + self, exc: type[BaseExcT_1] | types.GenericAlias, expected: str + ) -> type[BaseExcT_1]: + if isinstance(exc, type) and issubclass(exc, BaseException): + if not issubclass(exc, Exception): + self.is_baseexception = True + return exc + # because RaisesGroup does not support variable number of exceptions there's + # still a use for RaisesExc(ExceptionGroup[Exception]). + origin_exc: type[BaseException] | None = get_origin(exc) + if origin_exc and issubclass(origin_exc, BaseExceptionGroup): + exc_type = get_args(exc)[0] + if ( + issubclass(origin_exc, ExceptionGroup) and exc_type in (Exception, Any) + ) or ( + issubclass(origin_exc, BaseExceptionGroup) + and exc_type in (BaseException, Any) + ): + if not isinstance(exc, Exception): + self.is_baseexception = True + return cast(type[BaseExcT_1], origin_exc) + else: + raise ValueError( + f"Only `ExceptionGroup[Exception]` or `BaseExceptionGroup[BaseException]` " + f"are accepted as generic types but got `{exc}`. " + f"As `raises` will catch all instances of the specified group regardless of the " + f"generic argument specific nested exceptions has to be checked " + f"with `RaisesGroup`." + ) + # unclear if the Type/ValueError distinction is even helpful here + msg = f"expected exception must be {expected}, not " + if isinstance(exc, type): + raise ValueError(msg + f"{exc.__name__!r}") + if isinstance(exc, BaseException): + raise TypeError(msg + f"an exception instance ({type(exc).__name__})") + raise TypeError(msg + repr(type(exc).__name__)) + + @property + def fail_reason(self) -> str | None: + """Set after a call to :meth:`matches` to give a human-readable reason for why the match failed. + When used as a context manager the string will be printed as the reason for the + test failing.""" + return self._fail_reason + + def _check_check( + self: AbstractRaises[BaseExcT_1], + exception: BaseExcT_1, + ) -> bool: + if self.check is None: + return True + + if self.check(exception): + return True + + check_repr = "" if self._nested else " " + repr_callable(self.check) + self._fail_reason = f"check{check_repr} did not return True" + return False + + # TODO: harmonize with ExceptionInfo.match + def _check_match(self, e: BaseException) -> bool: + if self.match is None or re.search( + self.match, + stringified_exception := stringify_exception( + e, include_subexception_msg=False + ), + ): + return True + + # if we're matching a group, make sure we're explicit to reduce confusion + # if they're trying to match an exception contained within the group + maybe_specify_type = ( + f" the `{_exception_type_name(type(e))}()`" + if isinstance(e, BaseExceptionGroup) + else "" + ) + if isinstance(self.rawmatch, str): + # TODO: it instructs to use `-v` to print leading text, but that doesn't work + # I also don't know if this is the proper entry point, or tool to use at all + from _pytest.assertion.util import _diff_text + from _pytest.assertion.util import dummy_highlighter + + diff = _diff_text(self.rawmatch, stringified_exception, dummy_highlighter) + self._fail_reason = ("\n" if diff[0][0] == "-" else "") + "\n".join(diff) + return False + + # I don't love "Regex"+"Input" vs something like "expected regex"+"exception message" + # when they're similar it's not always obvious which is which + self._fail_reason = ( + f"Regex pattern did not match{maybe_specify_type}.\n" + f" Regex: {_match_pattern(self.match)!r}\n" + f" Input: {stringified_exception!r}" + ) + if _match_pattern(self.match) == stringified_exception: + self._fail_reason += "\n Did you mean to `re.escape()` the regex?" + return False + + @abstractmethod + def matches( + self: AbstractRaises[BaseExcT_1], exception: BaseException + ) -> TypeGuard[BaseExcT_1]: + """Check if an exception matches the requirements of this AbstractRaises. + If it fails, :meth:`AbstractRaises.fail_reason` should be set. + """ + + +@final +class RaisesExc(AbstractRaises[BaseExcT_co_default]): + """ + .. versionadded:: 8.4 + + + This is the class constructed when calling :func:`pytest.raises`, but may be used + directly as a helper class with :class:`RaisesGroup` when you want to specify + requirements on sub-exceptions. + + You don't need this if you only want to specify the type, since :class:`RaisesGroup` + accepts ``type[BaseException]``. + + :param type[BaseException] | tuple[type[BaseException]] | None expected_exception: + The expected type, or one of several possible types. + May be ``None`` in order to only make use of ``match`` and/or ``check`` + + The type is checked with :func:`isinstance`, and does not need to be an exact match. + If that is wanted you can use the ``check`` parameter. + + :kwparam str | Pattern[str] match: + A regex to match. + + :kwparam Callable[[BaseException], bool] check: + If specified, a callable that will be called with the exception as a parameter + after checking the type and the match regex if specified. + If it returns ``True`` it will be considered a match, if not it will + be considered a failed match. + + :meth:`RaisesExc.matches` can also be used standalone to check individual exceptions. + + Examples:: + + with RaisesGroup(RaisesExc(ValueError, match="string")) + ... + with RaisesGroup(RaisesExc(check=lambda x: x.args == (3, "hello"))): + ... + with RaisesGroup(RaisesExc(check=lambda x: type(x) is ValueError)): + ... + """ + + # Trio bundled hypothesis monkeypatching, we will probably instead assume that + # hypothesis will handle that in their pytest plugin by the time this is released. + # Alternatively we could add a version of get_pretty_function_description ourselves + # https://github.com/HypothesisWorks/hypothesis/blob/8ced2f59f5c7bea3344e35d2d53e1f8f8eb9fcd8/hypothesis-python/src/hypothesis/internal/reflection.py#L439 + + # At least one of the three parameters must be passed. + @overload + def __init__( + self, + expected_exception: ( + type[BaseExcT_co_default] | tuple[type[BaseExcT_co_default], ...] + ), + /, + *, + match: str | Pattern[str] | None = ..., + check: Callable[[BaseExcT_co_default], bool] | None = ..., + ) -> None: ... + + @overload + def __init__( + self: RaisesExc[BaseException], # Give E a value. + /, + *, + match: str | Pattern[str] | None, + # If exception_type is not provided, check() must do any typechecks itself. + check: Callable[[BaseException], bool] | None = ..., + ) -> None: ... + + @overload + def __init__(self, /, *, check: Callable[[BaseException], bool]) -> None: ... + + def __init__( + self, + expected_exception: ( + type[BaseExcT_co_default] | tuple[type[BaseExcT_co_default], ...] | None + ) = None, + /, + *, + match: str | Pattern[str] | None = None, + check: Callable[[BaseExcT_co_default], bool] | None = None, + ): + super().__init__(match=match, check=check) + if isinstance(expected_exception, tuple): + expected_exceptions = expected_exception + elif expected_exception is None: + expected_exceptions = () + else: + expected_exceptions = (expected_exception,) + + if (expected_exceptions == ()) and match is None and check is None: + raise ValueError("You must specify at least one parameter to match on.") + + self.expected_exceptions = tuple( + self._parse_exc(e, expected="a BaseException type") + for e in expected_exceptions + ) + + self._just_propagate = False + + def matches( + self, + exception: BaseException | None, + ) -> TypeGuard[BaseExcT_co_default]: + """Check if an exception matches the requirements of this :class:`RaisesExc`. + If it fails, :attr:`RaisesExc.fail_reason` will be set. + + Examples:: + + assert RaisesExc(ValueError).matches(my_exception): + # is equivalent to + assert isinstance(my_exception, ValueError) + + # this can be useful when checking e.g. the ``__cause__`` of an exception. + with pytest.raises(ValueError) as excinfo: + ... + assert RaisesExc(SyntaxError, match="foo").matches(excinfo.value.__cause__) + # above line is equivalent to + assert isinstance(excinfo.value.__cause__, SyntaxError) + assert re.search("foo", str(excinfo.value.__cause__) + + """ + self._just_propagate = False + if exception is None: + self._fail_reason = "exception is None" + return False + if not self._check_type(exception): + self._just_propagate = True + return False + + if not self._check_match(exception): + return False + + return self._check_check(exception) + + def __repr__(self) -> str: + parameters = [] + if self.expected_exceptions: + parameters.append(_exception_type_name(self.expected_exceptions)) + if self.match is not None: + # If no flags were specified, discard the redundant re.compile() here. + parameters.append( + f"match={_match_pattern(self.match)!r}", + ) + if self.check is not None: + parameters.append(f"check={repr_callable(self.check)}") + return f"RaisesExc({', '.join(parameters)})" + + def _check_type(self, exception: BaseException) -> TypeGuard[BaseExcT_co_default]: + self._fail_reason = _check_raw_type(self.expected_exceptions, exception) + return self._fail_reason is None + + def __enter__(self) -> ExceptionInfo[BaseExcT_co_default]: + self.excinfo: ExceptionInfo[BaseExcT_co_default] = ExceptionInfo.for_later() + return self.excinfo + + # TODO: move common code into superclass + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + if not self.expected_exceptions: + fail("DID NOT RAISE any exception") + if len(self.expected_exceptions) > 1: + fail(f"DID NOT RAISE any of {self.expected_exceptions!r}") + + fail(f"DID NOT RAISE {self.expected_exceptions[0]!r}") + + assert self.excinfo is not None, ( + "Internal error - should have been constructed in __enter__" + ) + + if not self.matches(exc_val): + if self._just_propagate: + return False + raise AssertionError(self._fail_reason) + + # Cast to narrow the exception type now that it's verified.... + # even though the TypeGuard in self.matches should be narrowing + exc_info = cast( + "tuple[type[BaseExcT_co_default], BaseExcT_co_default, types.TracebackType]", + (exc_type, exc_val, exc_tb), + ) + self.excinfo.fill_unfilled(exc_info) + return True + + +@final +class RaisesGroup(AbstractRaises[BaseExceptionGroup[BaseExcT_co]]): + """ + .. versionadded:: 8.4 + + Contextmanager for checking for an expected :exc:`ExceptionGroup`. + This works similar to :func:`pytest.raises`, but allows for specifying the structure of an :exc:`ExceptionGroup`. + :meth:`ExceptionInfo.group_contains` also tries to handle exception groups, + but it is very bad at checking that you *didn't* get unexpected exceptions. + + The catching behaviour differs from :ref:`except* `, being much + stricter about the structure by default. + By using ``allow_unwrapped=True`` and ``flatten_subgroups=True`` you can match + :ref:`except* ` fully when expecting a single exception. + + :param args: + Any number of exception types, :class:`RaisesGroup` or :class:`RaisesExc` + to specify the exceptions contained in this exception. + All specified exceptions must be present in the raised group, *and no others*. + + If you expect a variable number of exceptions you need to use + :func:`pytest.raises(ExceptionGroup) ` and manually check + the contained exceptions. Consider making use of :meth:`RaisesExc.matches`. + + It does not care about the order of the exceptions, so + ``RaisesGroup(ValueError, TypeError)`` + is equivalent to + ``RaisesGroup(TypeError, ValueError)``. + :kwparam str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception group and its :pep:`678` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + Note that " (5 subgroups)" will be stripped from the ``repr`` before matching. + :kwparam Callable[[E], bool] check: + If specified, a callable that will be called with the group as a parameter + after successfully matching the expected exceptions. If it returns ``True`` + it will be considered a match, if not it will be considered a failed match. + :kwparam bool allow_unwrapped: + If expecting a single exception or :class:`RaisesExc` it will match even + if the exception is not inside an exceptiongroup. + + Using this together with ``match``, ``check`` or expecting multiple exceptions + will raise an error. + :kwparam bool flatten_subgroups: + "flatten" any groups inside the raised exception group, extracting all exceptions + inside any nested groups, before matching. Without this it expects you to + fully specify the nesting structure by passing :class:`RaisesGroup` as expected + parameter. + + Examples:: + + with RaisesGroup(ValueError): + raise ExceptionGroup("", (ValueError(),)) + # match + with RaisesGroup( + ValueError, + ValueError, + RaisesExc(TypeError, match="^expected int$"), + match="^my group$", + ): + raise ExceptionGroup( + "my group", + [ + ValueError(), + TypeError("expected int"), + ValueError(), + ], + ) + # check + with RaisesGroup( + KeyboardInterrupt, + match="^hello$", + check=lambda x: isinstance(x.__cause__, ValueError), + ): + raise BaseExceptionGroup("hello", [KeyboardInterrupt()]) from ValueError + # nested groups + with RaisesGroup(RaisesGroup(ValueError)): + raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),)) + + # flatten_subgroups + with RaisesGroup(ValueError, flatten_subgroups=True): + raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),)) + + # allow_unwrapped + with RaisesGroup(ValueError, allow_unwrapped=True): + raise ValueError + + + :meth:`RaisesGroup.matches` can also be used directly to check a standalone exception group. + + + The matching algorithm is greedy, which means cases such as this may fail:: + + with RaisesGroup(ValueError, RaisesExc(ValueError, match="hello")): + raise ExceptionGroup("", (ValueError("hello"), ValueError("goodbye"))) + + even though it generally does not care about the order of the exceptions in the group. + To avoid the above you should specify the first :exc:`ValueError` with a :class:`RaisesExc` as well. + + .. note:: + When raised exceptions don't match the expected ones, you'll get a detailed error + message explaining why. This includes ``repr(check)`` if set, which in Python can be + overly verbose, showing memory locations etc etc. + + If installed and imported (in e.g. ``conftest.py``), the ``hypothesis`` library will + monkeypatch this output to provide shorter & more readable repr's. + """ + + # allow_unwrapped=True requires: singular exception, exception not being + # RaisesGroup instance, match is None, check is None + @overload + def __init__( + self, + expected_exception: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + /, + *, + allow_unwrapped: Literal[True], + flatten_subgroups: bool = False, + ) -> None: ... + + # flatten_subgroups = True also requires no nested RaisesGroup + @overload + def __init__( + self, + expected_exception: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + /, + *other_exceptions: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + flatten_subgroups: Literal[True], + match: str | Pattern[str] | None = None, + check: Callable[[BaseExceptionGroup[BaseExcT_co]], bool] | None = None, + ) -> None: ... + + # simplify the typevars if possible (the following 3 are equivalent but go simpler->complicated) + # ... the first handles RaisesGroup[ValueError], the second RaisesGroup[ExceptionGroup[ValueError]], + # the third RaisesGroup[ValueError | ExceptionGroup[ValueError]]. + # ... otherwise, we will get results like RaisesGroup[ValueError | ExceptionGroup[Never]] (I think) + # (technically correct but misleading) + @overload + def __init__( + self: RaisesGroup[ExcT_1], + expected_exception: type[ExcT_1] | RaisesExc[ExcT_1], + /, + *other_exceptions: type[ExcT_1] | RaisesExc[ExcT_1], + match: str | Pattern[str] | None = None, + check: Callable[[ExceptionGroup[ExcT_1]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[ExceptionGroup[ExcT_2]], + expected_exception: RaisesGroup[ExcT_2], + /, + *other_exceptions: RaisesGroup[ExcT_2], + match: str | Pattern[str] | None = None, + check: Callable[[ExceptionGroup[ExceptionGroup[ExcT_2]]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[ExcT_1 | ExceptionGroup[ExcT_2]], + expected_exception: type[ExcT_1] | RaisesExc[ExcT_1] | RaisesGroup[ExcT_2], + /, + *other_exceptions: type[ExcT_1] | RaisesExc[ExcT_1] | RaisesGroup[ExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[[ExceptionGroup[ExcT_1 | ExceptionGroup[ExcT_2]]], bool] | None + ) = None, + ) -> None: ... + + # same as the above 3 but handling BaseException + @overload + def __init__( + self: RaisesGroup[BaseExcT_1], + expected_exception: type[BaseExcT_1] | RaisesExc[BaseExcT_1], + /, + *other_exceptions: type[BaseExcT_1] | RaisesExc[BaseExcT_1], + match: str | Pattern[str] | None = None, + check: Callable[[BaseExceptionGroup[BaseExcT_1]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[BaseExceptionGroup[BaseExcT_2]], + expected_exception: RaisesGroup[BaseExcT_2], + /, + *other_exceptions: RaisesGroup[BaseExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[[BaseExceptionGroup[BaseExceptionGroup[BaseExcT_2]]], bool] | None + ) = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]], + expected_exception: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + /, + *other_exceptions: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[ + [BaseExceptionGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]]], + bool, + ] + | None + ) = None, + ) -> None: ... + + def __init__( + self: RaisesGroup[ExcT_1 | BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]], + expected_exception: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + /, + *other_exceptions: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + allow_unwrapped: bool = False, + flatten_subgroups: bool = False, + match: str | Pattern[str] | None = None, + check: ( + Callable[[BaseExceptionGroup[BaseExcT_1]], bool] + | Callable[[ExceptionGroup[ExcT_1]], bool] + | None + ) = None, + ): + # The type hint on the `self` and `check` parameters uses different formats + # that are *very* hard to reconcile while adhering to the overloads, so we cast + # it to avoid an error when passing it to super().__init__ + check = cast( + "Callable[[BaseExceptionGroup[ExcT_1|BaseExcT_1|BaseExceptionGroup[BaseExcT_2]]], bool]", + check, + ) + super().__init__(match=match, check=check) + self.allow_unwrapped = allow_unwrapped + self.flatten_subgroups: bool = flatten_subgroups + self.is_baseexception = False + + if allow_unwrapped and other_exceptions: + raise ValueError( + "You cannot specify multiple exceptions with `allow_unwrapped=True.`" + " If you want to match one of multiple possible exceptions you should" + " use a `RaisesExc`." + " E.g. `RaisesExc(check=lambda e: isinstance(e, (...)))`", + ) + if allow_unwrapped and isinstance(expected_exception, RaisesGroup): + raise ValueError( + "`allow_unwrapped=True` has no effect when expecting a `RaisesGroup`." + " You might want it in the expected `RaisesGroup`, or" + " `flatten_subgroups=True` if you don't care about the structure.", + ) + if allow_unwrapped and (match is not None or check is not None): + raise ValueError( + "`allow_unwrapped=True` bypasses the `match` and `check` parameters" + " if the exception is unwrapped. If you intended to match/check the" + " exception you should use a `RaisesExc` object. If you want to match/check" + " the exceptiongroup when the exception *is* wrapped you need to" + " do e.g. `if isinstance(exc.value, ExceptionGroup):" + " assert RaisesGroup(...).matches(exc.value)` afterwards.", + ) + + self.expected_exceptions: tuple[ + type[BaseExcT_co] | RaisesExc[BaseExcT_co] | RaisesGroup[BaseException], ... + ] = tuple( + self._parse_excgroup(e, "a BaseException type, RaisesExc, or RaisesGroup") + for e in ( + expected_exception, + *other_exceptions, + ) + ) + + def _parse_excgroup( + self, + exc: ( + type[BaseExcT_co] + | types.GenericAlias + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2] + ), + expected: str, + ) -> type[BaseExcT_co] | RaisesExc[BaseExcT_1] | RaisesGroup[BaseExcT_2]: + # verify exception type and set `self.is_baseexception` + if isinstance(exc, RaisesGroup): + if self.flatten_subgroups: + raise ValueError( + "You cannot specify a nested structure inside a RaisesGroup with" + " `flatten_subgroups=True`. The parameter will flatten subgroups" + " in the raised exceptiongroup before matching, which would never" + " match a nested structure.", + ) + self.is_baseexception |= exc.is_baseexception + exc._nested = True + return exc + elif isinstance(exc, RaisesExc): + self.is_baseexception |= exc.is_baseexception + exc._nested = True + return exc + elif isinstance(exc, tuple): + raise TypeError( + f"expected exception must be {expected}, not {type(exc).__name__!r}.\n" + "RaisesGroup does not support tuples of exception types when expecting one of " + "several possible exception types like RaisesExc.\n" + "If you meant to expect a group with multiple exceptions, list them as separate arguments." + ) + else: + return super()._parse_exc(exc, expected) + + @overload + def __enter__( + self: RaisesGroup[ExcT_1], + ) -> ExceptionInfo[ExceptionGroup[ExcT_1]]: ... + @overload + def __enter__( + self: RaisesGroup[BaseExcT_1], + ) -> ExceptionInfo[BaseExceptionGroup[BaseExcT_1]]: ... + + def __enter__(self) -> ExceptionInfo[BaseExceptionGroup[BaseException]]: + self.excinfo: ExceptionInfo[BaseExceptionGroup[BaseExcT_co]] = ( + ExceptionInfo.for_later() + ) + return self.excinfo + + def __repr__(self) -> str: + reqs = [ + e.__name__ if isinstance(e, type) else repr(e) + for e in self.expected_exceptions + ] + if self.allow_unwrapped: + reqs.append(f"allow_unwrapped={self.allow_unwrapped}") + if self.flatten_subgroups: + reqs.append(f"flatten_subgroups={self.flatten_subgroups}") + if self.match is not None: + # If no flags were specified, discard the redundant re.compile() here. + reqs.append(f"match={_match_pattern(self.match)!r}") + if self.check is not None: + reqs.append(f"check={repr_callable(self.check)}") + return f"RaisesGroup({', '.join(reqs)})" + + def _unroll_exceptions( + self, + exceptions: Sequence[BaseException], + ) -> Sequence[BaseException]: + """Used if `flatten_subgroups=True`.""" + res: list[BaseException] = [] + for exc in exceptions: + if isinstance(exc, BaseExceptionGroup): + res.extend(self._unroll_exceptions(exc.exceptions)) + + else: + res.append(exc) + return res + + @overload + def matches( + self: RaisesGroup[ExcT_1], + exception: BaseException | None, + ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ... + @overload + def matches( + self: RaisesGroup[BaseExcT_1], + exception: BaseException | None, + ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ... + + def matches( + self, + exception: BaseException | None, + ) -> bool: + """Check if an exception matches the requirements of this RaisesGroup. + If it fails, `RaisesGroup.fail_reason` will be set. + + Example:: + + with pytest.raises(TypeError) as excinfo: + ... + assert RaisesGroup(ValueError).matches(excinfo.value.__cause__) + # the above line is equivalent to + myexc = excinfo.value.__cause + assert isinstance(myexc, BaseExceptionGroup) + assert len(myexc.exceptions) == 1 + assert isinstance(myexc.exceptions[0], ValueError) + """ + self._fail_reason = None + if exception is None: + self._fail_reason = "exception is None" + return False + if not isinstance(exception, BaseExceptionGroup): + # we opt to only print type of the exception here, as the repr would + # likely be quite long + not_group_msg = f"`{type(exception).__name__}()` is not an exception group" + if len(self.expected_exceptions) > 1: + self._fail_reason = not_group_msg + return False + # if we have 1 expected exception, check if it would work even if + # allow_unwrapped is not set + res = self._check_expected(self.expected_exceptions[0], exception) + if res is None and self.allow_unwrapped: + return True + + if res is None: + self._fail_reason = ( + f"{not_group_msg}, but would match with `allow_unwrapped=True`" + ) + elif self.allow_unwrapped: + self._fail_reason = res + else: + self._fail_reason = not_group_msg + return False + + actual_exceptions: Sequence[BaseException] = exception.exceptions + if self.flatten_subgroups: + actual_exceptions = self._unroll_exceptions(actual_exceptions) + + if not self._check_match(exception): + self._fail_reason = cast(str, self._fail_reason) + old_reason = self._fail_reason + if ( + len(actual_exceptions) == len(self.expected_exceptions) == 1 + and isinstance(expected := self.expected_exceptions[0], type) + and isinstance(actual := actual_exceptions[0], expected) + and self._check_match(actual) + ): + assert self.match is not None, "can't be None if _check_match failed" + assert self._fail_reason is old_reason is not None + self._fail_reason += ( + f"\n" + f" but matched the expected `{self._repr_expected(expected)}`.\n" + f" You might want " + f"`RaisesGroup(RaisesExc({expected.__name__}, match={_match_pattern(self.match)!r}))`" + ) + else: + self._fail_reason = old_reason + return False + + # do the full check on expected exceptions + if not self._check_exceptions( + exception, + actual_exceptions, + ): + self._fail_reason = cast(str, self._fail_reason) + assert self._fail_reason is not None + old_reason = self._fail_reason + # if we're not expecting a nested structure, and there is one, do a second + # pass where we try flattening it + if ( + not self.flatten_subgroups + and not any( + isinstance(e, RaisesGroup) for e in self.expected_exceptions + ) + and any(isinstance(e, BaseExceptionGroup) for e in actual_exceptions) + and self._check_exceptions( + exception, + self._unroll_exceptions(exception.exceptions), + ) + ): + # only indent if it's a single-line reason. In a multi-line there's already + # indented lines that this does not belong to. + indent = " " if "\n" not in self._fail_reason else "" + self._fail_reason = ( + old_reason + + f"\n{indent}Did you mean to use `flatten_subgroups=True`?" + ) + else: + self._fail_reason = old_reason + return False + + # Only run `self.check` once we know `exception` is of the correct type. + if not self._check_check(exception): + reason = ( + cast(str, self._fail_reason) + f" on the {type(exception).__name__}" + ) + if ( + len(actual_exceptions) == len(self.expected_exceptions) == 1 + and isinstance(expected := self.expected_exceptions[0], type) + # we explicitly break typing here :) + and self._check_check(actual_exceptions[0]) # type: ignore[arg-type] + ): + self._fail_reason = reason + ( + f", but did return True for the expected {self._repr_expected(expected)}." + f" You might want RaisesGroup(RaisesExc({expected.__name__}, check=<...>))" + ) + else: + self._fail_reason = reason + return False + + return True + + @staticmethod + def _check_expected( + expected_type: ( + type[BaseException] | RaisesExc[BaseException] | RaisesGroup[BaseException] + ), + exception: BaseException, + ) -> str | None: + """Helper method for `RaisesGroup.matches` and `RaisesGroup._check_exceptions` + to check one of potentially several expected exceptions.""" + if isinstance(expected_type, type): + return _check_raw_type(expected_type, exception) + res = expected_type.matches(exception) + if res: + return None + assert expected_type.fail_reason is not None + if expected_type.fail_reason.startswith("\n"): + return f"\n{expected_type!r}: {indent(expected_type.fail_reason, ' ')}" + return f"{expected_type!r}: {expected_type.fail_reason}" + + @staticmethod + def _repr_expected(e: type[BaseException] | AbstractRaises[BaseException]) -> str: + """Get the repr of an expected type/RaisesExc/RaisesGroup, but we only want + the name if it's a type""" + if isinstance(e, type): + return _exception_type_name(e) + return repr(e) + + @overload + def _check_exceptions( + self: RaisesGroup[ExcT_1], + _exception: Exception, + actual_exceptions: Sequence[Exception], + ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ... + @overload + def _check_exceptions( + self: RaisesGroup[BaseExcT_1], + _exception: BaseException, + actual_exceptions: Sequence[BaseException], + ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ... + + def _check_exceptions( + self, + _exception: BaseException, + actual_exceptions: Sequence[BaseException], + ) -> bool: + """Helper method for RaisesGroup.matches that attempts to pair up expected and actual exceptions""" + # The _exception parameter is not used, but necessary for the TypeGuard + + # full table with all results + results = ResultHolder(self.expected_exceptions, actual_exceptions) + + # (indexes of) raised exceptions that haven't (yet) found an expected + remaining_actual = list(range(len(actual_exceptions))) + # (indexes of) expected exceptions that haven't found a matching raised + failed_expected: list[int] = [] + # successful greedy matches + matches: dict[int, int] = {} + + # loop over expected exceptions first to get a more predictable result + for i_exp, expected in enumerate(self.expected_exceptions): + for i_rem in remaining_actual: + res = self._check_expected(expected, actual_exceptions[i_rem]) + results.set_result(i_exp, i_rem, res) + if res is None: + remaining_actual.remove(i_rem) + matches[i_exp] = i_rem + break + else: + failed_expected.append(i_exp) + + # All exceptions matched up successfully + if not remaining_actual and not failed_expected: + return True + + # in case of a single expected and single raised we simplify the output + if 1 == len(actual_exceptions) == len(self.expected_exceptions): + assert not matches + self._fail_reason = res + return False + + # The test case is failing, so we can do a slow and exhaustive check to find + # duplicate matches etc that will be helpful in debugging + for i_exp, expected in enumerate(self.expected_exceptions): + for i_actual, actual in enumerate(actual_exceptions): + if results.has_result(i_exp, i_actual): + continue + results.set_result( + i_exp, i_actual, self._check_expected(expected, actual) + ) + + successful_str = ( + f"{len(matches)} matched exception{'s' if len(matches) > 1 else ''}. " + if matches + else "" + ) + + # all expected were found + if not failed_expected and results.no_match_for_actual(remaining_actual): + self._fail_reason = ( + f"{successful_str}Unexpected exception(s):" + f" {[actual_exceptions[i] for i in remaining_actual]!r}" + ) + return False + # all raised exceptions were expected + if not remaining_actual and results.no_match_for_expected(failed_expected): + no_match_for_str = ", ".join( + self._repr_expected(self.expected_exceptions[i]) + for i in failed_expected + ) + self._fail_reason = f"{successful_str}Too few exceptions raised, found no match for: [{no_match_for_str}]" + return False + + # if there's only one remaining and one failed, and the unmatched didn't match anything else, + # we elect to only print why the remaining and the failed didn't match. + if ( + 1 == len(remaining_actual) == len(failed_expected) + and results.no_match_for_actual(remaining_actual) + and results.no_match_for_expected(failed_expected) + ): + self._fail_reason = f"{successful_str}{results.get_result(failed_expected[0], remaining_actual[0])}" + return False + + # there's both expected and raised exceptions without matches + s = "" + if matches: + s += f"\n{successful_str}" + indent_1 = " " * 2 + indent_2 = " " * 4 + + if not remaining_actual: + s += "\nToo few exceptions raised!" + elif not failed_expected: + s += "\nUnexpected exception(s)!" + + if failed_expected: + s += "\nThe following expected exceptions did not find a match:" + rev_matches = {v: k for k, v in matches.items()} + for i_failed in failed_expected: + s += ( + f"\n{indent_1}{self._repr_expected(self.expected_exceptions[i_failed])}" + ) + for i_actual, actual in enumerate(actual_exceptions): + if results.get_result(i_exp, i_actual) is None: + # we print full repr of match target + s += ( + f"\n{indent_2}It matches {backquote(repr(actual))} which was paired with " + + backquote( + self._repr_expected( + self.expected_exceptions[rev_matches[i_actual]] + ) + ) + ) + + if remaining_actual: + s += "\nThe following raised exceptions did not find a match" + for i_actual in remaining_actual: + s += f"\n{indent_1}{actual_exceptions[i_actual]!r}:" + for i_exp, expected in enumerate(self.expected_exceptions): + res = results.get_result(i_exp, i_actual) + if i_exp in failed_expected: + assert res is not None + if res[0] != "\n": + s += "\n" + s += indent(res, indent_2) + if res is None: + # we print full repr of match target + s += ( + f"\n{indent_2}It matches {backquote(self._repr_expected(expected))} " + f"which was paired with {backquote(repr(actual_exceptions[matches[i_exp]]))}" + ) + + if len(self.expected_exceptions) == len(actual_exceptions) and possible_match( + results + ): + s += ( + "\nThere exist a possible match when attempting an exhaustive check," + " but RaisesGroup uses a greedy algorithm. " + "Please make your expected exceptions more stringent with `RaisesExc` etc" + " so the greedy algorithm can function." + ) + self._fail_reason = s + return False + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + fail(f"DID NOT RAISE any exception, expected `{self.expected_type()}`") + + assert self.excinfo is not None, ( + "Internal error - should have been constructed in __enter__" + ) + + # group_str is the only thing that differs between RaisesExc and RaisesGroup... + # I might just scrap it? Or make it part of fail_reason + group_str = ( + "(group)" + if self.allow_unwrapped and not issubclass(exc_type, BaseExceptionGroup) + else "group" + ) + + if not self.matches(exc_val): + fail(f"Raised exception {group_str} did not match: {self._fail_reason}") + + # Cast to narrow the exception type now that it's verified.... + # even though the TypeGuard in self.matches should be narrowing + exc_info = cast( + "tuple[type[BaseExceptionGroup[BaseExcT_co]], BaseExceptionGroup[BaseExcT_co], types.TracebackType]", + (exc_type, exc_val, exc_tb), + ) + self.excinfo.fill_unfilled(exc_info) + return True + + def expected_type(self) -> str: + subexcs = [] + for e in self.expected_exceptions: + if isinstance(e, RaisesExc): + subexcs.append(repr(e)) + elif isinstance(e, RaisesGroup): + subexcs.append(e.expected_type()) + elif isinstance(e, type): + subexcs.append(e.__name__) + else: # pragma: no cover + raise AssertionError("unknown type") + group_type = "Base" if self.is_baseexception else "" + return f"{group_type}ExceptionGroup({', '.join(subexcs)})" + + +@final +class NotChecked: + """Singleton for unchecked values in ResultHolder""" + + +class ResultHolder: + """Container for results of checking exceptions. + Used in RaisesGroup._check_exceptions and possible_match. + """ + + def __init__( + self, + expected_exceptions: tuple[ + type[BaseException] | AbstractRaises[BaseException], ... + ], + actual_exceptions: Sequence[BaseException], + ) -> None: + self.results: list[list[str | type[NotChecked] | None]] = [ + [NotChecked for _ in expected_exceptions] for _ in actual_exceptions + ] + + def set_result(self, expected: int, actual: int, result: str | None) -> None: + self.results[actual][expected] = result + + def get_result(self, expected: int, actual: int) -> str | None: + res = self.results[actual][expected] + assert res is not NotChecked + # mypy doesn't support identity checking against anything but None + return res # type: ignore[return-value] + + def has_result(self, expected: int, actual: int) -> bool: + return self.results[actual][expected] is not NotChecked + + def no_match_for_expected(self, expected: list[int]) -> bool: + for i in expected: + for actual_results in self.results: + assert actual_results[i] is not NotChecked + if actual_results[i] is None: + return False + return True + + def no_match_for_actual(self, actual: list[int]) -> bool: + for i in actual: + for res in self.results[i]: + assert res is not NotChecked + if res is None: + return False + return True + + +def possible_match(results: ResultHolder, used: set[int] | None = None) -> bool: + if used is None: + used = set() + curr_row = len(used) + if curr_row == len(results.results): + return True + return any( + val is None and i not in used and possible_match(results, used | {i}) + for (i, val) in enumerate(results.results[curr_row]) + ) diff --git a/.venv/lib/python3.11/site-packages/_pytest/recwarn.py b/.venv/lib/python3.11/site-packages/_pytest/recwarn.py new file mode 100644 index 00000000..440e3efa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/recwarn.py @@ -0,0 +1,365 @@ +# mypy: allow-untyped-defs +"""Record warnings during test function execution.""" + +from __future__ import annotations + +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterator +from pprint import pformat +import re +from types import TracebackType +from typing import Any +from typing import final +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar + + +if TYPE_CHECKING: + from typing_extensions import Self + +import warnings + +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.outcomes import Exit +from _pytest.outcomes import fail + + +T = TypeVar("T") + + +@fixture +def recwarn() -> Generator[WarningsRecorder]: + """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. + + See :ref:`warnings` for information on warning categories. + """ + wrec = WarningsRecorder(_ispytest=True) + with wrec: + warnings.simplefilter("default") + yield wrec + + +@overload +def deprecated_call( + *, match: str | re.Pattern[str] | None = ... +) -> WarningsRecorder: ... + + +@overload +def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: ... + + +def deprecated_call( + func: Callable[..., Any] | None = None, *args: Any, **kwargs: Any +) -> WarningsRecorder | Any: + """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``. + + This function can be used as a context manager:: + + >>> import warnings + >>> def api_call_v2(): + ... warnings.warn('use v3 of this api', DeprecationWarning) + ... return 200 + + >>> import pytest + >>> with pytest.deprecated_call(): + ... assert api_call_v2() == 200 + + It can also be used by passing a function and ``*args`` and ``**kwargs``, + in which case it will ensure calling ``func(*args, **kwargs)`` produces one of + the warnings types above. The return value is the return value of the function. + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex. + + The context manager produces a list of :class:`warnings.WarningMessage` objects, + one for each warning raised. + """ + __tracebackhide__ = True + if func is not None: + args = (func, *args) + return warns( + (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs + ) + + +@overload +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...] = ..., + *, + match: str | re.Pattern[str] | None = ..., +) -> WarningsChecker: ... + + +@overload +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...], + func: Callable[..., T], + *args: Any, + **kwargs: Any, +) -> T: ... + + +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, + *args: Any, + match: str | re.Pattern[str] | None = None, + **kwargs: Any, +) -> WarningsChecker | Any: + r"""Assert that code raises a particular class of warning. + + Specifically, the parameter ``expected_warning`` can be a warning class or tuple + of warning classes, and the code inside the ``with`` block must issue at least one + warning of that class or classes. + + This helper produces a list of :class:`warnings.WarningMessage` objects, one for + each warning emitted (regardless of whether it is an ``expected_warning`` or not). + Since pytest 8.0, unmatched warnings are also re-emitted when the context closes. + + This function can be used as a context manager:: + + >>> import pytest + >>> with pytest.warns(RuntimeWarning): + ... warnings.warn("my warning", RuntimeWarning) + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex:: + + >>> with pytest.warns(UserWarning, match='must be 0 or None'): + ... warnings.warn("value must be 0 or None", UserWarning) + + >>> with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("value must be 42", UserWarning) + + >>> with pytest.warns(UserWarning): # catch re-emitted warning + ... with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("this is not here", UserWarning) + Traceback (most recent call last): + ... + Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted... + + **Using with** ``pytest.mark.parametrize`` + + When using :ref:`pytest.mark.parametrize ref` it is possible to parametrize tests + such that some runs raise a warning and others do not. + + This could be achieved in the same way as with exceptions, see + :ref:`parametrizing_conditional_raising` for an example. + + """ + __tracebackhide__ = True + if not args: + if kwargs: + argnames = ", ".join(sorted(kwargs)) + raise TypeError( + f"Unexpected keyword arguments passed to pytest.warns: {argnames}" + "\nUse context-manager form instead?" + ) + return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) + else: + func = args[0] + if not callable(func): + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + with WarningsChecker(expected_warning, _ispytest=True): + return func(*args[1:], **kwargs) + + +class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] + """A context manager to record raised warnings. + + Each recorded warning is an instance of :class:`warnings.WarningMessage`. + + Adapted from `warnings.catch_warnings`. + + .. note:: + ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated + differently; see :ref:`ensuring_function_triggers`. + + """ + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + super().__init__(record=True) + self._entered = False + self._list: list[warnings.WarningMessage] = [] + + @property + def list(self) -> list[warnings.WarningMessage]: + """The list of recorded warnings.""" + return self._list + + def __getitem__(self, i: int) -> warnings.WarningMessage: + """Get a recorded warning by index.""" + return self._list[i] + + def __iter__(self) -> Iterator[warnings.WarningMessage]: + """Iterate through the recorded warnings.""" + return iter(self._list) + + def __len__(self) -> int: + """The number of recorded warnings.""" + return len(self._list) + + def pop(self, cls: type[Warning] = Warning) -> warnings.WarningMessage: + """Pop the first recorded warning which is an instance of ``cls``, + but not an instance of a child class of any other match. + Raises ``AssertionError`` if there is no match. + """ + best_idx: int | None = None + for i, w in enumerate(self._list): + if w.category == cls: + return self._list.pop(i) # exact match, stop looking + if issubclass(w.category, cls) and ( + best_idx is None + or not issubclass(w.category, self._list[best_idx].category) + ): + best_idx = i + if best_idx is not None: + return self._list.pop(best_idx) + __tracebackhide__ = True + raise AssertionError(f"{cls!r} not found in warning list") + + def clear(self) -> None: + """Clear the list of recorded warnings.""" + self._list[:] = [] + + def __enter__(self) -> Self: + if self._entered: + __tracebackhide__ = True + raise RuntimeError(f"Cannot enter {self!r} twice") + _list = super().__enter__() + # record=True means it's None. + assert _list is not None + self._list = _list + warnings.simplefilter("always") + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if not self._entered: + __tracebackhide__ = True + raise RuntimeError(f"Cannot exit {self!r} without entering first") + + super().__exit__(exc_type, exc_val, exc_tb) + + # Built-in catch_warnings does not reset entered state so we do it + # manually here for this context manager to become reusable. + self._entered = False + + +@final +class WarningsChecker(WarningsRecorder): + def __init__( + self, + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, + match_expr: str | re.Pattern[str] | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + super().__init__(_ispytest=True) + + msg = "exceptions must be derived from Warning, not %s" + if isinstance(expected_warning, tuple): + for exc in expected_warning: + if not issubclass(exc, Warning): + raise TypeError(msg % type(exc)) + expected_warning_tup = expected_warning + elif isinstance(expected_warning, type) and issubclass( + expected_warning, Warning + ): + expected_warning_tup = (expected_warning,) + else: + raise TypeError(msg % type(expected_warning)) + + self.expected_warning = expected_warning_tup + self.match_expr = match_expr + + def matches(self, warning: warnings.WarningMessage) -> bool: + assert self.expected_warning is not None + return issubclass(warning.category, self.expected_warning) and bool( + self.match_expr is None or re.search(self.match_expr, str(warning.message)) + ) + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + super().__exit__(exc_type, exc_val, exc_tb) + + __tracebackhide__ = True + + # BaseExceptions like pytest.{skip,fail,xfail,exit} or Ctrl-C within + # pytest.warns should *not* trigger "DID NOT WARN" and get suppressed + # when the warning doesn't happen. Control-flow exceptions should always + # propagate. + if exc_val is not None and ( + not isinstance(exc_val, Exception) + # Exit is an Exception, not a BaseException, for some reason. + or isinstance(exc_val, Exit) + ): + return + + def found_str() -> str: + return pformat([record.message for record in self], indent=2) + + try: + if not any(issubclass(w.category, self.expected_warning) for w in self): + fail( + f"DID NOT WARN. No warnings of type {self.expected_warning} were emitted.\n" + f" Emitted warnings: {found_str()}." + ) + elif not any(self.matches(w) for w in self): + fail( + f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n" + f" Regex: {self.match_expr}\n" + f" Emitted warnings: {found_str()}." + ) + finally: + # Whether or not any warnings matched, we want to re-emit all unmatched warnings. + for w in self: + if not self.matches(w): + warnings.warn_explicit( + message=w.message, + category=w.category, + filename=w.filename, + lineno=w.lineno, + module=w.__module__, + source=w.source, + ) + + # Currently in Python it is possible to pass other types than an + # `str` message when creating `Warning` instances, however this + # causes an exception when :func:`warnings.filterwarnings` is used + # to filter those warnings. See + # https://github.com/python/cpython/issues/103577 for a discussion. + # While this can be considered a bug in CPython, we put guards in + # pytest as the error message produced without this check in place + # is confusing (#10865). + for w in self: + if type(w.message) is not UserWarning: + # If the warning was of an incorrect type then `warnings.warn()` + # creates a UserWarning. Any other warning must have been specified + # explicitly. + continue + if not w.message.args: + # UserWarning() without arguments must have been specified explicitly. + continue + msg = w.message.args[0] + if isinstance(msg, str): + continue + # It's possible that UserWarning was explicitly specified, and + # its first argument was not a string. But that case can't be + # distinguished from an invalid type. + raise TypeError( + f"Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})" + ) diff --git a/.venv/lib/python3.11/site-packages/_pytest/reports.py b/.venv/lib/python3.11/site-packages/_pytest/reports.py new file mode 100644 index 00000000..480ffae1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/reports.py @@ -0,0 +1,637 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +from io import StringIO +import os +from pprint import pprint +from typing import Any +from typing import cast +from typing import final +from typing import Literal +from typing import NoReturn +from typing import TYPE_CHECKING + +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprEntry +from _pytest._code.code import ReprEntryNative +from _pytest._code.code import ReprExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import ReprFuncArgs +from _pytest._code.code import ReprLocals +from _pytest._code.code import ReprTraceback +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.config import Config +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import skip + + +if TYPE_CHECKING: + from typing_extensions import Self + + from _pytest.runner import CallInfo + + +def getworkerinfoline(node): + try: + return node._workerinfocache + except AttributeError: + d = node.workerinfo + ver = "{}.{}.{}".format(*d["version_info"][:3]) + node._workerinfocache = s = "[{}] {} -- Python {} {}".format( + d["id"], d["sysplatform"], ver, d["executable"] + ) + return s + + +class BaseReport: + when: str | None + location: tuple[str, int | None, str] | None + longrepr: ( + None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr + ) + sections: list[tuple[str, str]] + nodeid: str + outcome: Literal["passed", "failed", "skipped"] + + def __init__(self, **kw: Any) -> None: + self.__dict__.update(kw) + + if TYPE_CHECKING: + # Can have arbitrary fields given to __init__(). + def __getattr__(self, key: str) -> Any: ... + + def toterminal(self, out: TerminalWriter) -> None: + if hasattr(self, "node"): + worker_info = getworkerinfoline(self.node) + if worker_info: + out.line(worker_info) + + longrepr = self.longrepr + if longrepr is None: + return + + if hasattr(longrepr, "toterminal"): + longrepr_terminal = cast(TerminalRepr, longrepr) + longrepr_terminal.toterminal(out) + else: + try: + s = str(longrepr) + except UnicodeEncodeError: + s = "" + out.line(s) + + def get_sections(self, prefix: str) -> Iterator[tuple[str, str]]: + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + + @property + def longreprtext(self) -> str: + """Read-only property that returns the full string representation of + ``longrepr``. + + .. versionadded:: 3.0 + """ + file = StringIO() + tw = TerminalWriter(file) + tw.hasmarkup = False + self.toterminal(tw) + exc = file.getvalue() + return exc.strip() + + @property + def caplog(self) -> str: + """Return captured log lines, if log capturing is enabled. + + .. versionadded:: 3.5 + """ + return "\n".join( + content for (prefix, content) in self.get_sections("Captured log") + ) + + @property + def capstdout(self) -> str: + """Return captured text from stdout, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stdout") + ) + + @property + def capstderr(self) -> str: + """Return captured text from stderr, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stderr") + ) + + @property + def passed(self) -> bool: + """Whether the outcome is passed.""" + return self.outcome == "passed" + + @property + def failed(self) -> bool: + """Whether the outcome is failed.""" + return self.outcome == "failed" + + @property + def skipped(self) -> bool: + """Whether the outcome is skipped.""" + return self.outcome == "skipped" + + @property + def fspath(self) -> str: + """The path portion of the reported node, as a string.""" + return self.nodeid.split("::")[0] + + @property + def count_towards_summary(self) -> bool: + """**Experimental** Whether this report should be counted towards the + totals shown at the end of the test session: "1 passed, 1 failure, etc". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + return True + + @property + def head_line(self) -> str | None: + """**Experimental** The head line shown with longrepr output for this + report, more commonly during traceback representation during + failures:: + + ________ Test.foo ________ + + + In the example above, the head_line is "Test.foo". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + if self.location is not None: + fspath, lineno, domain = self.location + return domain + return None + + def _get_verbose_word_with_markup( + self, config: Config, default_markup: Mapping[str, bool] + ) -> tuple[str, Mapping[str, bool]]: + _category, _short, verbose = config.hook.pytest_report_teststatus( + report=self, config=config + ) + + if isinstance(verbose, str): + return verbose, default_markup + + if isinstance(verbose, Sequence) and len(verbose) == 2: + word, markup = verbose + if isinstance(word, str) and isinstance(markup, Mapping): + return word, markup + + fail( # pragma: no cover + "pytest_report_teststatus() hook (from a plugin) returned " + f"an invalid verbose value: {verbose!r}.\nExpected either a string " + "or a tuple of (word, markup)." + ) + + def _to_json(self) -> dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + return _report_to_json(self) + + @classmethod + def _from_json(cls, reportdict: dict[str, object]) -> Self: + """Create either a TestReport or CollectReport, depending on the calling class. + + It is the callers responsibility to know which class to pass here. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + kwargs = _report_kwargs_from_json(reportdict) + return cls(**kwargs) + + +def _report_unserialization_failure( + type_name: str, report_class: type[BaseReport], reportdict +) -> NoReturn: + url = "https://github.com/pytest-dev/pytest/issues" + stream = StringIO() + pprint("-" * 100, stream=stream) + pprint(f"INTERNALERROR: Unknown entry type returned: {type_name}", stream=stream) + pprint(f"report_name: {report_class}", stream=stream) + pprint(reportdict, stream=stream) + pprint(f"Please report this bug at {url}", stream=stream) + pprint("-" * 100, stream=stream) + raise RuntimeError(stream.getvalue()) + + +@final +class TestReport(BaseReport): + """Basic test report object (also used for setup and teardown calls if + they fail). + + Reports can contain arbitrary extra attributes. + """ + + __test__ = False + + # Defined by skipping plugin. + # xfail reason if xfailed, otherwise not defined. Use hasattr to distinguish. + wasxfail: str + + def __init__( + self, + nodeid: str, + location: tuple[str, int | None, str], + keywords: Mapping[str, Any], + outcome: Literal["passed", "failed", "skipped"], + longrepr: None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr, + when: Literal["setup", "call", "teardown"], + sections: Iterable[tuple[str, str]] = (), + duration: float = 0, + start: float = 0, + stop: float = 0, + user_properties: Iterable[tuple[str, object]] | None = None, + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: A (filesystempath, lineno, domaininfo) tuple indicating the + #: actual location of a test item - it might be different from the + #: collected one e.g. if a method is inherited from a different module. + #: The filesystempath may be relative to ``config.rootdir``. + #: The line number is 0-based. + self.location: tuple[str, int | None, str] = location + + #: A name -> value dictionary containing all keywords and + #: markers associated with a test invocation. + self.keywords: Mapping[str, Any] = keywords + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: One of 'setup', 'call', 'teardown' to indicate runtest phase. + self.when: Literal["setup", "call", "teardown"] = when + + #: User properties is a list of tuples (name, value) that holds user + #: defined properties of the test. + self.user_properties = list(user_properties or []) + + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. + self.sections = list(sections) + + #: Time it took to run just the test. + self.duration: float = duration + + #: The system time when the call started, in seconds since the epoch. + self.start: float = start + #: The system time when the call ended, in seconds since the epoch. + self.stop: float = stop + + self.__dict__.update(extra) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" + + @classmethod + def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: + """Create and fill a TestReport with standard item and call info. + + :param item: The item. + :param call: The call info. + """ + when = call.when + # Remove "collect" from the Literal type -- only for collection calls. + assert when != "collect" + duration = call.duration + start = call.start + stop = call.stop + keywords = {x: 1 for x in item.keywords} + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome: Literal["passed", "failed", "skipped"] = "passed" + longrepr: ( + None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr + ) = None + else: + if not isinstance(excinfo, ExceptionInfo): + outcome = "failed" + longrepr = excinfo + elif isinstance(excinfo.value, skip.Exception): + outcome = "skipped" + r = excinfo._getreprcrash() + assert r is not None, ( + "There should always be a traceback entry for skipping a test." + ) + if excinfo.value._use_item_location: + path, line = item.reportinfo()[:2] + assert line is not None + longrepr = os.fspath(path), line + 1, r.message + else: + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: # exception in setup or teardown + longrepr = item._repr_failure_py( + excinfo, style=item.config.getoption("tbstyle", "auto") + ) + for rwhen, key, content in item._report_sections: + sections.append((f"Captured {key} {rwhen}", content)) + return cls( + item.nodeid, + item.location, + keywords, + outcome, + longrepr, + when, + sections, + duration, + start, + stop, + user_properties=item.user_properties, + ) + + +@final +class CollectReport(BaseReport): + """Collection report object. + + Reports can contain arbitrary extra attributes. + """ + + when = "collect" + + def __init__( + self, + nodeid: str, + outcome: Literal["passed", "failed", "skipped"], + longrepr: None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr, + result: list[Item | Collector] | None, + sections: Iterable[tuple[str, str]] = (), + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: The collected items and collection nodes. + self.result = result or [] + + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. + self.sections = list(sections) + + self.__dict__.update(extra) + + @property + def location( # type:ignore[override] + self, + ) -> tuple[str, int | None, str] | None: + return (self.fspath, None, self.fspath) + + def __repr__(self) -> str: + return f"" + + +class CollectErrorRepr(TerminalRepr): + def __init__(self, msg: str) -> None: + self.longrepr = msg + + def toterminal(self, out: TerminalWriter) -> None: + out.line(self.longrepr, red=True) + + +def pytest_report_to_serializable( + report: CollectReport | TestReport, +) -> dict[str, Any] | None: + if isinstance(report, (TestReport, CollectReport)): + data = report._to_json() + data["$report_type"] = report.__class__.__name__ + return data + # TODO: Check if this is actually reachable. + return None # type: ignore[unreachable] + + +def pytest_report_from_serializable( + data: dict[str, Any], +) -> CollectReport | TestReport | None: + if "$report_type" in data: + if data["$report_type"] == "TestReport": + return TestReport._from_json(data) + elif data["$report_type"] == "CollectReport": + return CollectReport._from_json(data) + assert False, "Unknown report_type unserialize data: {}".format( + data["$report_type"] + ) + return None + + +def _report_to_json(report: BaseReport) -> dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def serialize_repr_entry( + entry: ReprEntry | ReprEntryNative, + ) -> dict[str, Any]: + data = dataclasses.asdict(entry) + for key, value in data.items(): + if hasattr(value, "__dict__"): + data[key] = dataclasses.asdict(value) + entry_data = {"type": type(entry).__name__, "data": data} + return entry_data + + def serialize_repr_traceback(reprtraceback: ReprTraceback) -> dict[str, Any]: + result = dataclasses.asdict(reprtraceback) + result["reprentries"] = [ + serialize_repr_entry(x) for x in reprtraceback.reprentries + ] + return result + + def serialize_repr_crash( + reprcrash: ReprFileLocation | None, + ) -> dict[str, Any] | None: + if reprcrash is not None: + return dataclasses.asdict(reprcrash) + else: + return None + + def serialize_exception_longrepr(rep: BaseReport) -> dict[str, Any]: + assert rep.longrepr is not None + # TODO: Investigate whether the duck typing is really necessary here. + longrepr = cast(ExceptionRepr, rep.longrepr) + result: dict[str, Any] = { + "reprcrash": serialize_repr_crash(longrepr.reprcrash), + "reprtraceback": serialize_repr_traceback(longrepr.reprtraceback), + "sections": longrepr.sections, + } + if isinstance(longrepr, ExceptionChainRepr): + result["chain"] = [] + for repr_traceback, repr_crash, description in longrepr.chain: + result["chain"].append( + ( + serialize_repr_traceback(repr_traceback), + serialize_repr_crash(repr_crash), + description, + ) + ) + else: + result["chain"] = None + return result + + d = report.__dict__.copy() + if hasattr(report.longrepr, "toterminal"): + if hasattr(report.longrepr, "reprtraceback") and hasattr( + report.longrepr, "reprcrash" + ): + d["longrepr"] = serialize_exception_longrepr(report) + else: + d["longrepr"] = str(report.longrepr) + else: + d["longrepr"] = report.longrepr + for name in d: + if isinstance(d[name], os.PathLike): + d[name] = os.fspath(d[name]) + elif name == "result": + d[name] = None # for now + return d + + +def _report_kwargs_from_json(reportdict: dict[str, Any]) -> dict[str, Any]: + """Return **kwargs that can be used to construct a TestReport or + CollectReport instance. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def deserialize_repr_entry(entry_data): + data = entry_data["data"] + entry_type = entry_data["type"] + if entry_type == "ReprEntry": + reprfuncargs = None + reprfileloc = None + reprlocals = None + if data["reprfuncargs"]: + reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) + if data["reprfileloc"]: + reprfileloc = ReprFileLocation(**data["reprfileloc"]) + if data["reprlocals"]: + reprlocals = ReprLocals(data["reprlocals"]["lines"]) + + reprentry: ReprEntry | ReprEntryNative = ReprEntry( + lines=data["lines"], + reprfuncargs=reprfuncargs, + reprlocals=reprlocals, + reprfileloc=reprfileloc, + style=data["style"], + ) + elif entry_type == "ReprEntryNative": + reprentry = ReprEntryNative(data["lines"]) + else: + _report_unserialization_failure(entry_type, TestReport, reportdict) + return reprentry + + def deserialize_repr_traceback(repr_traceback_dict): + repr_traceback_dict["reprentries"] = [ + deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"] + ] + return ReprTraceback(**repr_traceback_dict) + + def deserialize_repr_crash(repr_crash_dict: dict[str, Any] | None): + if repr_crash_dict is not None: + return ReprFileLocation(**repr_crash_dict) + else: + return None + + if ( + reportdict["longrepr"] + and "reprcrash" in reportdict["longrepr"] + and "reprtraceback" in reportdict["longrepr"] + ): + reprtraceback = deserialize_repr_traceback( + reportdict["longrepr"]["reprtraceback"] + ) + reprcrash = deserialize_repr_crash(reportdict["longrepr"]["reprcrash"]) + if reportdict["longrepr"]["chain"]: + chain = [] + for repr_traceback_data, repr_crash_data, description in reportdict[ + "longrepr" + ]["chain"]: + chain.append( + ( + deserialize_repr_traceback(repr_traceback_data), + deserialize_repr_crash(repr_crash_data), + description, + ) + ) + exception_info: ExceptionChainRepr | ReprExceptionInfo = ExceptionChainRepr( + chain + ) + else: + exception_info = ReprExceptionInfo( + reprtraceback=reprtraceback, + reprcrash=reprcrash, + ) + + for section in reportdict["longrepr"]["sections"]: + exception_info.addsection(*section) + reportdict["longrepr"] = exception_info + + return reportdict diff --git a/.venv/lib/python3.11/site-packages/_pytest/runner.py b/.venv/lib/python3.11/site-packages/_pytest/runner.py new file mode 100644 index 00000000..26e4e838 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/runner.py @@ -0,0 +1,571 @@ +# mypy: allow-untyped-defs +"""Basic collect and runtest protocol implementations.""" + +from __future__ import annotations + +import bdb +from collections.abc import Callable +import dataclasses +import os +import sys +import types +from typing import cast +from typing import final +from typing import Generic +from typing import Literal +from typing import TYPE_CHECKING +from typing import TypeVar + +from .reports import BaseReport +from .reports import CollectErrorRepr +from .reports import CollectReport +from .reports import TestReport +from _pytest import timing +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.nodes import Collector +from _pytest.nodes import Directory +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.outcomes import Exit +from _pytest.outcomes import OutcomeException +from _pytest.outcomes import Skipped +from _pytest.outcomes import TEST_OUTCOME + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + +if TYPE_CHECKING: + from _pytest.main import Session + from _pytest.terminal import TerminalReporter + +# +# pytest plugin hooks. + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "Reporting", after="general") + group.addoption( + "--durations", + action="store", + type=int, + default=None, + metavar="N", + help="Show N slowest setup/test durations (N=0 for all)", + ) + group.addoption( + "--durations-min", + action="store", + type=float, + default=None, + metavar="N", + help="Minimal duration in seconds for inclusion in slowest list. " + "Default: 0.005 (or 0.0 if -vv is given).", + ) + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + durations = terminalreporter.config.option.durations + durations_min = terminalreporter.config.option.durations_min + verbose = terminalreporter.config.get_verbosity() + if durations is None: + return + if durations_min is None: + durations_min = 0.005 if verbose < 2 else 0.0 + tr = terminalreporter + dlist = [] + for replist in tr.stats.values(): + for rep in replist: + if hasattr(rep, "duration"): + dlist.append(rep) + if not dlist: + return + dlist.sort(key=lambda x: x.duration, reverse=True) + if not durations: + tr.write_sep("=", "slowest durations") + else: + tr.write_sep("=", f"slowest {durations} durations") + dlist = dlist[:durations] + + for i, rep in enumerate(dlist): + if rep.duration < durations_min: + tr.write_line("") + message = f"({len(dlist) - i} durations < {durations_min:g}s hidden." + if terminalreporter.config.option.durations_min is None: + message += " Use -vv to show these durations." + message += ")" + tr.write_line(message) + break + tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") + + +def pytest_sessionstart(session: Session) -> None: + session._setupstate = SetupState() + + +def pytest_sessionfinish(session: Session) -> None: + session._setupstate.teardown_exact(None) + + +def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> bool: + ihook = item.ihook + ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) + runtestprotocol(item, nextitem=nextitem) + ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location) + return True + + +def runtestprotocol( + item: Item, log: bool = True, nextitem: Item | None = None +) -> list[TestReport]: + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: # type: ignore[attr-defined] + # This only happens if the item is re-run, as is done by + # pytest-rerunfailures. + item._initrequest() # type: ignore[attr-defined] + rep = call_and_report(item, "setup", log) + reports = [rep] + if rep.passed: + if item.config.getoption("setupshow", False): + show_test_item(item) + if not item.config.getoption("setuponly", False): + reports.append(call_and_report(item, "call", log)) + # If the session is about to fail or stop, teardown everything - this is + # necessary to correctly report fixture teardown errors (see #11706) + if item.session.shouldfail or item.session.shouldstop: + nextitem = None + reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) + # After all teardown hooks have been called + # want funcargs and request info to go away. + if hasrequest: + item._request = False # type: ignore[attr-defined] + item.funcargs = None # type: ignore[attr-defined] + return reports + + +def show_test_item(item: Item) -> None: + """Show test function, parameters and the fixtures of the test item.""" + tw = item.config.get_terminal_writer() + tw.line() + tw.write(" " * 8) + tw.write(item.nodeid) + used_fixtures = sorted(getattr(item, "fixturenames", [])) + if used_fixtures: + tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) + tw.flush() + + +def pytest_runtest_setup(item: Item) -> None: + _update_current_test_var(item, "setup") + item.session._setupstate.setup(item) + + +def pytest_runtest_call(item: Item) -> None: + _update_current_test_var(item, "call") + try: + del sys.last_type + del sys.last_value + del sys.last_traceback + if sys.version_info >= (3, 12, 0): + del sys.last_exc # type:ignore[attr-defined] + except AttributeError: + pass + try: + item.runtest() + except Exception as e: + # Store trace info to allow postmortem debugging + sys.last_type = type(e) + sys.last_value = e + if sys.version_info >= (3, 12, 0): + sys.last_exc = e # type:ignore[attr-defined] + assert e.__traceback__ is not None + # Skip *this* frame + sys.last_traceback = e.__traceback__.tb_next + raise + + +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + _update_current_test_var(item, "teardown") + item.session._setupstate.teardown_exact(nextitem) + _update_current_test_var(item, None) + + +def _update_current_test_var( + item: Item, when: Literal["setup", "call", "teardown"] | None +) -> None: + """Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage. + + If ``when`` is None, delete ``PYTEST_CURRENT_TEST`` from the environment. + """ + var_name = "PYTEST_CURRENT_TEST" + if when: + value = f"{item.nodeid} ({when})" + # don't allow null bytes on environment variables (see #2644, #2957) + value = value.replace("\x00", "(null)") + os.environ[var_name] = value + else: + os.environ.pop(var_name) + + +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if report.when in ("setup", "teardown"): + if report.failed: + # category, shortletter, verbose-word + return "error", "E", "ERROR" + elif report.skipped: + return "skipped", "s", "SKIPPED" + else: + return "", "", "" + return None + + +# +# Implementation + + +def call_and_report( + item: Item, when: Literal["setup", "call", "teardown"], log: bool = True, **kwds +) -> TestReport: + ihook = item.ihook + if when == "setup": + runtest_hook: Callable[..., None] = ihook.pytest_runtest_setup + elif when == "call": + runtest_hook = ihook.pytest_runtest_call + elif when == "teardown": + runtest_hook = ihook.pytest_runtest_teardown + else: + assert False, f"Unhandled runtest hook case: {when}" + reraise: tuple[type[BaseException], ...] = (Exit,) + if not item.config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + call = CallInfo.from_call( + lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise + ) + report: TestReport = ihook.pytest_runtest_makereport(item=item, call=call) + if log: + ihook.pytest_runtest_logreport(report=report) + if check_interactive_exception(call, report): + ihook.pytest_exception_interact(node=item, call=call, report=report) + return report + + +def check_interactive_exception(call: CallInfo[object], report: BaseReport) -> bool: + """Check whether the call raised an exception that should be reported as + interactive.""" + if call.excinfo is None: + # Didn't raise. + return False + if hasattr(report, "wasxfail"): + # Exception was expected. + return False + if isinstance(call.excinfo.value, (Skipped, bdb.BdbQuit)): + # Special control flow exception. + return False + return True + + +TResult = TypeVar("TResult", covariant=True) + + +@final +@dataclasses.dataclass +class CallInfo(Generic[TResult]): + """Result/Exception info of a function invocation.""" + + _result: TResult | None + #: The captured exception of the call, if it raised. + excinfo: ExceptionInfo[BaseException] | None + #: The system time when the call started, in seconds since the epoch. + start: float + #: The system time when the call ended, in seconds since the epoch. + stop: float + #: The call duration, in seconds. + duration: float + #: The context of invocation: "collect", "setup", "call" or "teardown". + when: Literal["collect", "setup", "call", "teardown"] + + def __init__( + self, + result: TResult | None, + excinfo: ExceptionInfo[BaseException] | None, + start: float, + stop: float, + duration: float, + when: Literal["collect", "setup", "call", "teardown"], + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._result = result + self.excinfo = excinfo + self.start = start + self.stop = stop + self.duration = duration + self.when = when + + @property + def result(self) -> TResult: + """The return value of the call, if it didn't raise. + + Can only be accessed if excinfo is None. + """ + if self.excinfo is not None: + raise AttributeError(f"{self!r} has no valid result") + # The cast is safe because an exception wasn't raised, hence + # _result has the expected function return type (which may be + # None, that's why a cast and not an assert). + return cast(TResult, self._result) + + @classmethod + def from_call( + cls, + func: Callable[[], TResult], + when: Literal["collect", "setup", "call", "teardown"], + reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None, + ) -> CallInfo[TResult]: + """Call func, wrapping the result in a CallInfo. + + :param func: + The function to call. Called without arguments. + :type func: Callable[[], _pytest.runner.TResult] + :param when: + The phase in which the function is called. + :param reraise: + Exception or exceptions that shall propagate if raised by the + function, instead of being wrapped in the CallInfo. + """ + excinfo = None + instant = timing.Instant() + try: + result: TResult | None = func() + except BaseException: + excinfo = ExceptionInfo.from_current() + if reraise is not None and isinstance(excinfo.value, reraise): + raise + result = None + duration = instant.elapsed() + return cls( + start=duration.start.time, + stop=duration.stop.time, + duration=duration.seconds, + when=when, + result=result, + excinfo=excinfo, + _ispytest=True, + ) + + def __repr__(self) -> str: + if self.excinfo is None: + return f"" + return f"" + + +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: + return TestReport.from_item_and_call(item, call) + + +def pytest_make_collect_report(collector: Collector) -> CollectReport: + def collect() -> list[Item | Collector]: + # Before collecting, if this is a Directory, load the conftests. + # If a conftest import fails to load, it is considered a collection + # error of the Directory collector. This is why it's done inside of the + # CallInfo wrapper. + # + # Note: initial conftests are loaded early, not here. + if isinstance(collector, Directory): + collector.config.pluginmanager._loadconftestmodules( + collector.path, + collector.config.getoption("importmode"), + rootpath=collector.config.rootpath, + consider_namespace_packages=collector.config.getini( + "consider_namespace_packages" + ), + ) + + return list(collector.collect()) + + call = CallInfo.from_call( + collect, "collect", reraise=(KeyboardInterrupt, SystemExit) + ) + longrepr: None | tuple[str, int, str] | str | TerminalRepr = None + if not call.excinfo: + outcome: Literal["passed", "skipped", "failed"] = "passed" + else: + skip_exceptions = [Skipped] + unittest = sys.modules.get("unittest") + if unittest is not None: + skip_exceptions.append(unittest.SkipTest) + if isinstance(call.excinfo.value, tuple(skip_exceptions)): + outcome = "skipped" + r_ = collector._repr_failure_py(call.excinfo, "line") + assert isinstance(r_, ExceptionChainRepr), repr(r_) + r = r_.reprcrash + assert r + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + errorinfo = collector.repr_failure(call.excinfo) + if not hasattr(errorinfo, "toterminal"): + assert isinstance(errorinfo, str) + errorinfo = CollectErrorRepr(errorinfo) + longrepr = errorinfo + result = call.result if not call.excinfo else None + rep = CollectReport(collector.nodeid, outcome, longrepr, result) + rep.call = call # type: ignore # see collect_one_node + return rep + + +class SetupState: + """Shared state for setting up/tearing down test items or collectors + in a session. + + Suppose we have a collection tree as follows: + + + + + + + + The SetupState maintains a stack. The stack starts out empty: + + [] + + During the setup phase of item1, setup(item1) is called. What it does + is: + + push session to stack, run session.setup() + push mod1 to stack, run mod1.setup() + push item1 to stack, run item1.setup() + + The stack is: + + [session, mod1, item1] + + While the stack is in this shape, it is allowed to add finalizers to + each of session, mod1, item1 using addfinalizer(). + + During the teardown phase of item1, teardown_exact(item2) is called, + where item2 is the next item to item1. What it does is: + + pop item1 from stack, run its teardowns + pop mod1 from stack, run its teardowns + + mod1 was popped because it ended its purpose with item1. The stack is: + + [session] + + During the setup phase of item2, setup(item2) is called. What it does + is: + + push mod2 to stack, run mod2.setup() + push item2 to stack, run item2.setup() + + Stack: + + [session, mod2, item2] + + During the teardown phase of item2, teardown_exact(None) is called, + because item2 is the last item. What it does is: + + pop item2 from stack, run its teardowns + pop mod2 from stack, run its teardowns + pop session from stack, run its teardowns + + Stack: + + [] + + The end! + """ + + def __init__(self) -> None: + # The stack is in the dict insertion order. + self.stack: dict[ + Node, + tuple[ + # Node's finalizers. + list[Callable[[], object]], + # Node's exception and original traceback, if its setup raised. + tuple[OutcomeException | Exception, types.TracebackType | None] | None, + ], + ] = {} + + def setup(self, item: Item) -> None: + """Setup objects along the collector chain to the item.""" + needed_collectors = item.listchain() + + # If a collector fails its setup, fail its entire subtree of items. + # The setup is not retried for each item - the same exception is used. + for col, (finalizers, exc) in self.stack.items(): + assert col in needed_collectors, "previous item was not torn down properly" + if exc: + raise exc[0].with_traceback(exc[1]) + + for col in needed_collectors[len(self.stack) :]: + assert col not in self.stack + # Push onto the stack. + self.stack[col] = ([col.teardown], None) + try: + col.setup() + except TEST_OUTCOME as exc: + self.stack[col] = (self.stack[col][0], (exc, exc.__traceback__)) + raise + + def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: + """Attach a finalizer to the given node. + + The node must be currently active in the stack. + """ + assert node and not isinstance(node, tuple) + assert callable(finalizer) + assert node in self.stack, (node, self.stack) + self.stack[node][0].append(finalizer) + + def teardown_exact(self, nextitem: Item | None) -> None: + """Teardown the current stack up until reaching nodes that nextitem + also descends from. + + When nextitem is None (meaning we're at the last item), the entire + stack is torn down. + """ + needed_collectors = (nextitem and nextitem.listchain()) or [] + exceptions: list[BaseException] = [] + while self.stack: + if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: + break + node, (finalizers, _) = self.stack.popitem() + these_exceptions = [] + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + these_exceptions.append(e) + + if len(these_exceptions) == 1: + exceptions.extend(these_exceptions) + elif these_exceptions: + msg = f"errors while tearing down {node!r}" + exceptions.append(BaseExceptionGroup(msg, these_exceptions[::-1])) + + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup("errors during test teardown", exceptions[::-1]) + if nextitem is None: + assert not self.stack + + +def collect_one_node(collector: Collector) -> CollectReport: + ihook = collector.ihook + ihook.pytest_collectstart(collector=collector) + rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) + call = rep.__dict__.pop("call", None) + if call and check_interactive_exception(call, rep): + ihook.pytest_exception_interact(node=collector, call=call, report=rep) + return rep diff --git a/.venv/lib/python3.11/site-packages/_pytest/scope.py b/.venv/lib/python3.11/site-packages/_pytest/scope.py new file mode 100644 index 00000000..2b007e87 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/scope.py @@ -0,0 +1,91 @@ +""" +Scope definition and related utilities. + +Those are defined here, instead of in the 'fixtures' module because +their use is spread across many other pytest modules, and centralizing it in 'fixtures' +would cause circular references. + +Also this makes the module light to import, as it should. +""" + +from __future__ import annotations + +from enum import Enum +from functools import total_ordering +from typing import Literal + + +_ScopeName = Literal["session", "package", "module", "class", "function"] + + +@total_ordering +class Scope(Enum): + """ + Represents one of the possible fixture scopes in pytest. + + Scopes are ordered from lower to higher, that is: + + ->>> higher ->>> + + Function < Class < Module < Package < Session + + <<<- lower <<<- + """ + + # Scopes need to be listed from lower to higher. + Function = "function" + Class = "class" + Module = "module" + Package = "package" + Session = "session" + + def next_lower(self) -> Scope: + """Return the next lower scope.""" + index = _SCOPE_INDICES[self] + if index == 0: + raise ValueError(f"{self} is the lower-most scope") + return _ALL_SCOPES[index - 1] + + def next_higher(self) -> Scope: + """Return the next higher scope.""" + index = _SCOPE_INDICES[self] + if index == len(_SCOPE_INDICES) - 1: + raise ValueError(f"{self} is the upper-most scope") + return _ALL_SCOPES[index + 1] + + def __lt__(self, other: Scope) -> bool: + self_index = _SCOPE_INDICES[self] + other_index = _SCOPE_INDICES[other] + return self_index < other_index + + @classmethod + def from_user( + cls, scope_name: _ScopeName, descr: str, where: str | None = None + ) -> Scope: + """ + Given a scope name from the user, return the equivalent Scope enum. Should be used + whenever we want to convert a user provided scope name to its enum object. + + If the scope name is invalid, construct a user friendly message and call pytest.fail. + """ + from _pytest.outcomes import fail + + try: + # Holding this reference is necessary for mypy at the moment. + scope = Scope(scope_name) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope_name + ), + pytrace=False, + ) + return scope + + +_ALL_SCOPES = list(Scope) +_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)} + + +# Ordered list of scopes which can contain many tests (in practice all except Function). +HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] diff --git a/.venv/lib/python3.11/site-packages/_pytest/setuponly.py b/.venv/lib/python3.11/site-packages/_pytest/setuponly.py new file mode 100644 index 00000000..7e6b46bc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/setuponly.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Generator + +from _pytest._io.saferepr import saferepr +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +from _pytest.scope import Scope +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setuponly", + "--setup-only", + action="store_true", + help="Only setup fixtures, do not execute tests", + ) + group.addoption( + "--setupshow", + "--setup-show", + action="store_true", + help="Show setup of fixtures while executing tests", + ) + + +@pytest.hookimpl(wrapper=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Generator[None, object, object]: + try: + return (yield) + finally: + if request.config.option.setupshow: + if hasattr(request, "param"): + # Save the fixture parameter so ._show_fixture_action() can + # display it now and during the teardown (in .finish()). + if fixturedef.ids: + if callable(fixturedef.ids): + param = fixturedef.ids(request.param) + else: + param = fixturedef.ids[request.param_index] + else: + param = request.param + fixturedef.cached_param = param # type: ignore[attr-defined] + _show_fixture_action(fixturedef, request.config, "SETUP") + + +def pytest_fixture_post_finalizer( + fixturedef: FixtureDef[object], request: SubRequest +) -> None: + if fixturedef.cached_result is not None: + config = request.config + if config.option.setupshow: + _show_fixture_action(fixturedef, request.config, "TEARDOWN") + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param + + +def _show_fixture_action( + fixturedef: FixtureDef[object], config: Config, msg: str +) -> None: + capman = config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture() + + tw = config.get_terminal_writer() + tw.line() + # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. + scope_indent = list(reversed(Scope)).index(fixturedef._scope) + tw.write(" " * 2 * scope_indent) + + scopename = fixturedef.scope[0].upper() + tw.write(f"{msg:<8} {scopename} {fixturedef.argname}") + + if msg == "SETUP": + deps = sorted(arg for arg in fixturedef.argnames if arg != "request") + if deps: + tw.write(" (fixtures used: {})".format(", ".join(deps))) + + if hasattr(fixturedef, "cached_param"): + tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") + + tw.flush() + + if capman: + capman.resume_global_capture() + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setuponly: + config.option.setupshow = True + return None diff --git a/.venv/lib/python3.11/site-packages/_pytest/setupplan.py b/.venv/lib/python3.11/site-packages/_pytest/setupplan.py new file mode 100644 index 00000000..4e124cce --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/setupplan.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setupplan", + "--setup-plan", + action="store_true", + help="Show what fixtures and tests would be executed but " + "don't execute anything", + ) + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> object | None: + # Will return a dummy fixture if the setuponly option is provided. + if request.config.option.setupplan: + my_cache_key = fixturedef.cache_key(request) + fixturedef.cached_result = (None, my_cache_key, None) + return fixturedef.cached_result + return None + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setupplan: + config.option.setuponly = True + config.option.setupshow = True + return None diff --git a/.venv/lib/python3.11/site-packages/_pytest/skipping.py b/.venv/lib/python3.11/site-packages/_pytest/skipping.py new file mode 100644 index 00000000..ec118f2c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/skipping.py @@ -0,0 +1,316 @@ +# mypy: allow-untyped-defs +"""Support for skip/xfail functions and markers.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +import dataclasses +import os +import platform +import sys +import traceback +from typing import Optional + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.mark.structures import Mark +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.raises import AbstractRaises +from _pytest.reports import BaseReport +from _pytest.reports import TestReport +from _pytest.runner import CallInfo +from _pytest.stash import StashKey + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--runxfail", + action="store_true", + dest="runxfail", + default=False, + help="Report the results of xfail tests as if they were not marked", + ) + + parser.addini( + "xfail_strict", + "Default for the strict parameter of xfail " + "markers when not given explicitly (default: False)", + default=False, + type="bool", + ) + + +def pytest_configure(config: Config) -> None: + if config.option.runxfail: + # yay a hack + import pytest + + old = pytest.xfail + config.add_cleanup(lambda: setattr(pytest, "xfail", old)) + + def nop(*args, **kwargs): + pass + + nop.Exception = xfail.Exception # type: ignore[attr-defined] + setattr(pytest, "xfail", nop) + + config.addinivalue_line( + "markers", + "skip(reason=None): skip the given test function with an optional reason. " + 'Example: skip(reason="no way of currently testing this") skips the ' + "test.", + ) + config.addinivalue_line( + "markers", + "skipif(condition, ..., *, reason=...): " + "skip the given test function if any of the conditions evaluate to True. " + "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " + "See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif", + ) + config.addinivalue_line( + "markers", + "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): " + "mark the test function as an expected failure if any of the conditions " + "evaluate to True. Optionally specify a reason for better reporting " + "and run=False if you don't even want to execute the test function. " + "If only specific exception(s) are expected, you can list them in " + "raises, and if the test fails in other ways, it will be reported as " + "a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail", + ) + + +def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, str]: + """Evaluate a single skipif/xfail condition. + + If an old-style string condition is given, it is eval()'d, otherwise the + condition is bool()'d. If this fails, an appropriately formatted pytest.fail + is raised. + + Returns (result, reason). The reason is only relevant if the result is True. + """ + # String condition. + if isinstance(condition, str): + globals_ = { + "os": os, + "sys": sys, + "platform": platform, + "config": item.config, + } + for dictionary in reversed( + item.ihook.pytest_markeval_namespace(config=item.config) + ): + if not isinstance(dictionary, Mapping): + raise ValueError( + f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}" + ) + globals_.update(dictionary) + if hasattr(item, "obj"): + globals_.update(item.obj.__globals__) + try: + filename = f"<{mark.name} condition>" + condition_code = compile(condition, filename, "eval") + result = eval(condition_code, globals_) + except SyntaxError as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition", + " " + condition, + " " + " " * (exc.offset or 0) + "^", + "SyntaxError: invalid syntax", + ] + fail("\n".join(msglines), pytrace=False) + except Exception as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition", + " " + condition, + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + # Boolean condition. + else: + try: + result = bool(condition) + except Exception as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition as a boolean", + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + reason = mark.kwargs.get("reason", None) + if reason is None: + if isinstance(condition, str): + reason = "condition: " + condition + else: + # XXX better be checked at collection time + msg = ( + f"Error evaluating {mark.name!r}: " + + "you need to specify reason=STRING when using booleans as conditions." + ) + fail(msg, pytrace=False) + + return result, reason + + +@dataclasses.dataclass(frozen=True) +class Skip: + """The result of evaluate_skip_marks().""" + + reason: str = "unconditional skip" + + +def evaluate_skip_marks(item: Item) -> Skip | None: + """Evaluate skip and skipif marks on item, returning Skip if triggered.""" + for mark in item.iter_markers(name="skipif"): + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Skip(reason) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Skip(reason) + + for mark in item.iter_markers(name="skip"): + try: + return Skip(*mark.args, **mark.kwargs) + except TypeError as e: + raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None + + return None + + +@dataclasses.dataclass(frozen=True) +class Xfail: + """The result of evaluate_xfail_marks().""" + + __slots__ = ("raises", "reason", "run", "strict") + + reason: str + run: bool + strict: bool + raises: ( + type[BaseException] + | tuple[type[BaseException], ...] + | AbstractRaises[BaseException] + | None + ) + + +def evaluate_xfail_marks(item: Item) -> Xfail | None: + """Evaluate xfail marks on item, returning Xfail if triggered.""" + for mark in item.iter_markers(name="xfail"): + run = mark.kwargs.get("run", True) + strict = mark.kwargs.get("strict", item.config.getini("xfail_strict")) + raises = mark.kwargs.get("raises", None) + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Xfail(reason, run, strict, raises) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Xfail(reason, run, strict, raises) + + return None + + +# Saves the xfail mark evaluation. Can be refreshed during call if None. +xfailed_key = StashKey[Optional[Xfail]]() + + +@hookimpl(tryfirst=True) +def pytest_runtest_setup(item: Item) -> None: + skipped = evaluate_skip_marks(item) + if skipped: + raise skip.Exception(skipped.reason, _use_item_location=True) + + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + +@hookimpl(wrapper=True) +def pytest_runtest_call(item: Item) -> Generator[None]: + xfailed = item.stash.get(xfailed_key, None) + if xfailed is None: + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + try: + return (yield) + finally: + # The test run may have added an xfail mark dynamically. + xfailed = item.stash.get(xfailed_key, None) + if xfailed is None: + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + +@hookimpl(wrapper=True) +def pytest_runtest_makereport( + item: Item, call: CallInfo[None] +) -> Generator[None, TestReport, TestReport]: + rep = yield + xfailed = item.stash.get(xfailed_key, None) + if item.config.option.runxfail: + pass # don't interfere + elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): + assert call.excinfo.value.msg is not None + rep.wasxfail = call.excinfo.value.msg + rep.outcome = "skipped" + elif not rep.skipped and xfailed: + if call.excinfo: + raises = xfailed.raises + if raises is None or ( + ( + isinstance(raises, (type, tuple)) + and isinstance(call.excinfo.value, raises) + ) + or ( + isinstance(raises, AbstractRaises) + and raises.matches(call.excinfo.value) + ) + ): + rep.outcome = "skipped" + rep.wasxfail = xfailed.reason + else: + rep.outcome = "failed" + elif call.when == "call": + if xfailed.strict: + rep.outcome = "failed" + rep.longrepr = "[XPASS(strict)] " + xfailed.reason + else: + rep.outcome = "passed" + rep.wasxfail = xfailed.reason + return rep + + +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if hasattr(report, "wasxfail"): + if report.skipped: + return "xfailed", "x", "XFAIL" + elif report.passed: + return "xpassed", "X", "XPASS" + return None diff --git a/.venv/lib/python3.11/site-packages/_pytest/stash.py b/.venv/lib/python3.11/site-packages/_pytest/stash.py new file mode 100644 index 00000000..6a9ff884 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/stash.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import Any +from typing import cast +from typing import Generic +from typing import TypeVar + + +__all__ = ["Stash", "StashKey"] + + +T = TypeVar("T") +D = TypeVar("D") + + +class StashKey(Generic[T]): + """``StashKey`` is an object used as a key to a :class:`Stash`. + + A ``StashKey`` is associated with the type ``T`` of the value of the key. + + A ``StashKey`` is unique and cannot conflict with another key. + + .. versionadded:: 7.0 + """ + + __slots__ = () + + +class Stash: + r"""``Stash`` is a type-safe heterogeneous mutable mapping that + allows keys and value types to be defined separately from + where it (the ``Stash``) is created. + + Usually you will be given an object which has a ``Stash``, for example + :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: + + .. code-block:: python + + stash: Stash = some_object.stash + + If a module or plugin wants to store data in this ``Stash``, it creates + :class:`StashKey`\s for its keys (at the module level): + + .. code-block:: python + + # At the top-level of the module + some_str_key = StashKey[str]() + some_bool_key = StashKey[bool]() + + To store information: + + .. code-block:: python + + # Value type must match the key. + stash[some_str_key] = "value" + stash[some_bool_key] = True + + To retrieve the information: + + .. code-block:: python + + # The static type of some_str is str. + some_str = stash[some_str_key] + # The static type of some_bool is bool. + some_bool = stash[some_bool_key] + + .. versionadded:: 7.0 + """ + + __slots__ = ("_storage",) + + def __init__(self) -> None: + self._storage: dict[StashKey[Any], object] = {} + + def __setitem__(self, key: StashKey[T], value: T) -> None: + """Set a value for key.""" + self._storage[key] = value + + def __getitem__(self, key: StashKey[T]) -> T: + """Get the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + return cast(T, self._storage[key]) + + def get(self, key: StashKey[T], default: D) -> T | D: + """Get the value for key, or return default if the key wasn't set + before.""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key: StashKey[T], default: T) -> T: + """Return the value of key if already set, otherwise set the value + of key to default and return default.""" + try: + return self[key] + except KeyError: + self[key] = default + return default + + def __delitem__(self, key: StashKey[T]) -> None: + """Delete the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + del self._storage[key] + + def __contains__(self, key: StashKey[T]) -> bool: + """Return whether key was set.""" + return key in self._storage + + def __len__(self) -> int: + """Return how many items exist in the stash.""" + return len(self._storage) diff --git a/.venv/lib/python3.11/site-packages/_pytest/stepwise.py b/.venv/lib/python3.11/site-packages/_pytest/stepwise.py new file mode 100644 index 00000000..8901540e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/stepwise.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import dataclasses +from datetime import datetime +from datetime import timedelta +from typing import Any +from typing import TYPE_CHECKING + +from _pytest import nodes +from _pytest.cacheprovider import Cache +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.main import Session +from _pytest.reports import TestReport + + +if TYPE_CHECKING: + from typing_extensions import Self + +STEPWISE_CACHE_DIR = "cache/stepwise" + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--sw", + "--stepwise", + action="store_true", + default=False, + dest="stepwise", + help="Exit on test failure and continue from last failing test next time", + ) + group.addoption( + "--sw-skip", + "--stepwise-skip", + action="store_true", + default=False, + dest="stepwise_skip", + help="Ignore the first failing test but stop on the next failing test. " + "Implicitly enables --stepwise.", + ) + group.addoption( + "--sw-reset", + "--stepwise-reset", + action="store_true", + default=False, + dest="stepwise_reset", + help="Resets stepwise state, restarting the stepwise workflow. " + "Implicitly enables --stepwise.", + ) + + +def pytest_configure(config: Config) -> None: + # --stepwise-skip/--stepwise-reset implies stepwise. + if config.option.stepwise_skip or config.option.stepwise_reset: + config.option.stepwise = True + if config.getoption("stepwise"): + config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") + + +def pytest_sessionfinish(session: Session) -> None: + if not session.config.getoption("stepwise"): + assert session.config.cache is not None + if hasattr(session.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + + +@dataclasses.dataclass +class StepwiseCacheInfo: + # The nodeid of the last failed test. + last_failed: str | None + + # The number of tests in the last time --stepwise was run. + # We use this information as a simple way to invalidate the cache information, avoiding + # confusing behavior in case the cache is stale. + last_test_count: int | None + + # The date when the cache was last updated, for information purposes only. + last_cache_date_str: str + + @property + def last_cache_date(self) -> datetime: + return datetime.fromisoformat(self.last_cache_date_str) + + @classmethod + def empty(cls) -> Self: + return cls( + last_failed=None, + last_test_count=None, + last_cache_date_str=datetime.now().isoformat(), + ) + + def update_date_to_now(self) -> None: + self.last_cache_date_str = datetime.now().isoformat() + + +class StepwisePlugin: + def __init__(self, config: Config) -> None: + self.config = config + self.session: Session | None = None + self.report_status: list[str] = [] + assert config.cache is not None + self.cache: Cache = config.cache + self.skip: bool = config.getoption("stepwise_skip") + self.reset: bool = config.getoption("stepwise_reset") + self.cached_info = self._load_cached_info() + + def _load_cached_info(self) -> StepwiseCacheInfo: + cached_dict: dict[str, Any] | None = self.cache.get(STEPWISE_CACHE_DIR, None) + if cached_dict: + try: + return StepwiseCacheInfo( + cached_dict["last_failed"], + cached_dict["last_test_count"], + cached_dict["last_cache_date_str"], + ) + except (KeyError, TypeError) as e: + error = f"{type(e).__name__}: {e}" + self.report_status.append(f"error reading cache, discarding ({error})") + + # Cache not found or error during load, return a new cache. + return StepwiseCacheInfo.empty() + + def pytest_sessionstart(self, session: Session) -> None: + self.session = session + + def pytest_collection_modifyitems( + self, config: Config, items: list[nodes.Item] + ) -> None: + last_test_count = self.cached_info.last_test_count + self.cached_info.last_test_count = len(items) + + if self.reset: + self.report_status.append("resetting state, not skipping.") + self.cached_info.last_failed = None + return + + if not self.cached_info.last_failed: + self.report_status.append("no previously failed tests, not skipping.") + return + + if last_test_count is not None and last_test_count != len(items): + self.report_status.append( + f"test count changed, not skipping (now {len(items)} tests, previously {last_test_count})." + ) + self.cached_info.last_failed = None + return + + # Check all item nodes until we find a match on last failed. + failed_index = None + for index, item in enumerate(items): + if item.nodeid == self.cached_info.last_failed: + failed_index = index + break + + # If the previously failed test was not found among the test items, + # do not skip any tests. + if failed_index is None: + self.report_status.append("previously failed test not found, not skipping.") + else: + cache_age = datetime.now() - self.cached_info.last_cache_date + # Round up to avoid showing microseconds. + cache_age = timedelta(seconds=int(cache_age.total_seconds())) + self.report_status.append( + f"skipping {failed_index} already passed items (cache from {cache_age} ago," + f" use --sw-reset to discard)." + ) + deselected = items[:failed_index] + del items[:failed_index] + config.hook.pytest_deselected(items=deselected) + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + if self.skip: + # Remove test from the failed ones (if it exists) and unset the skip option + # to make sure the following tests will not be skipped. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + self.skip = False + else: + # Mark test as the last failing and interrupt the test session. + self.cached_info.last_failed = report.nodeid + assert self.session is not None + self.session.shouldstop = ( + "Test failed, continuing from this test next run." + ) + + else: + # If the test was actually run and did pass. + if report.when == "call": + # Remove test from the failed ones, if exists. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + def pytest_report_collectionfinish(self) -> list[str] | None: + if self.config.get_verbosity() >= 0 and self.report_status: + return [f"stepwise: {x}" for x in self.report_status] + return None + + def pytest_sessionfinish(self) -> None: + if hasattr(self.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + self.cached_info.update_date_to_now() + self.cache.set(STEPWISE_CACHE_DIR, dataclasses.asdict(self.cached_info)) diff --git a/.venv/lib/python3.11/site-packages/_pytest/terminal.py b/.venv/lib/python3.11/site-packages/_pytest/terminal.py new file mode 100644 index 00000000..a95f79ba --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/terminal.py @@ -0,0 +1,1643 @@ +# mypy: allow-untyped-defs +"""Terminal reporting of the full testing process. + +This is a good source for looking at the various reporting hooks. +""" + +from __future__ import annotations + +import argparse +from collections import Counter +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import datetime +from functools import partial +import inspect +from pathlib import Path +import platform +import sys +import textwrap +from typing import Any +from typing import ClassVar +from typing import final +from typing import Literal +from typing import NamedTuple +from typing import TextIO +from typing import TYPE_CHECKING +import warnings + +import pluggy + +from _pytest import compat +from _pytest import nodes +from _pytest import timing +from _pytest._code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._io import TerminalWriter +from _pytest._io.wcwidth import wcswidth +import _pytest._version +from _pytest.assertion.util import running_on_ci +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.reports import BaseReport +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +if TYPE_CHECKING: + from _pytest.main import Session + + +REPORT_COLLECTING_RESOLUTION = 0.5 + +KNOWN_TYPES = ( + "failed", + "passed", + "skipped", + "deselected", + "xfailed", + "xpassed", + "warnings", + "error", +) + +_REPORTCHARS_DEFAULT = "fE" + + +class MoreQuietAction(argparse.Action): + """A modified copy of the argparse count action which counts down and updates + the legacy quiet attribute at the same time. + + Used to unify verbosity handling. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: object = None, + required: bool = False, + help: str | None = None, + ) -> None: + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + default=default, + required=required, + help=help, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: str | Sequence[object] | None, + option_string: str | None = None, + ) -> None: + new_count = getattr(namespace, self.dest, 0) - 1 + setattr(namespace, self.dest, new_count) + # todo Deprecate config.quiet + namespace.quiet = getattr(namespace, "quiet", 0) + 1 + + +class TestShortLogReport(NamedTuple): + """Used to store the test status result category, shortletter and verbose word. + For example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :ivar category: + The class of result, for example ``“passed”``, ``“skipped”``, ``“error”``, or the empty string. + + :ivar letter: + The short letter shown as testing progresses, for example ``"."``, ``"s"``, ``"E"``, or the empty string. + + :ivar word: + Verbose word is shown as testing progresses in verbose mode, for example ``"PASSED"``, ``"SKIPPED"``, + ``"ERROR"``, or the empty string. + """ + + category: str + letter: str + word: str | tuple[str, Mapping[str, bool]] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "Reporting", after="general") + group._addoption( # private to use reserved lower-case short option + "-v", + "--verbose", + action="count", + default=0, + dest="verbose", + help="Increase verbosity", + ) + group.addoption( + "--no-header", + action="store_true", + default=False, + dest="no_header", + help="Disable header", + ) + group.addoption( + "--no-summary", + action="store_true", + default=False, + dest="no_summary", + help="Disable summary", + ) + group.addoption( + "--no-fold-skipped", + action="store_false", + dest="fold_skipped", + default=True, + help="Do not fold skipped tests in short summary.", + ) + group.addoption( + "--force-short-summary", + action="store_true", + dest="force_short_summary", + default=False, + help="Force condensed summary output regardless of verbosity level.", + ) + group._addoption( # private to use reserved lower-case short option + "-q", + "--quiet", + action=MoreQuietAction, + default=0, + dest="verbose", + help="Decrease verbosity", + ) + group.addoption( + "--verbosity", + dest="verbose", + type=int, + default=0, + help="Set verbosity. Default: 0.", + ) + group._addoption( # private to use reserved lower-case short option + "-r", + action="store", + dest="reportchars", + default=_REPORTCHARS_DEFAULT, + metavar="chars", + help="Show extra test summary info as specified by chars: (f)ailed, " + "(E)rror, (s)kipped, (x)failed, (X)passed, " + "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " + "(w)arnings are enabled by default (see --disable-warnings), " + "'N' can be used to reset the list. (default: 'fE').", + ) + group.addoption( + "--disable-warnings", + "--disable-pytest-warnings", + default=False, + dest="disable_warnings", + action="store_true", + help="Disable warnings summary", + ) + group._addoption( # private to use reserved lower-case short option + "-l", + "--showlocals", + action="store_true", + dest="showlocals", + default=False, + help="Show locals in tracebacks (disabled by default)", + ) + group.addoption( + "--no-showlocals", + action="store_false", + dest="showlocals", + help="Hide locals in tracebacks (negate --showlocals passed through addopts)", + ) + group.addoption( + "--tb", + metavar="style", + action="store", + dest="tbstyle", + default="auto", + choices=["auto", "long", "short", "no", "line", "native"], + help="Traceback print mode (auto/long/short/line/native/no)", + ) + group.addoption( + "--xfail-tb", + action="store_true", + dest="xfail_tb", + default=False, + help="Show tracebacks for xfail (as long as --tb != no)", + ) + group.addoption( + "--show-capture", + action="store", + dest="showcapture", + choices=["no", "stdout", "stderr", "log", "all"], + default="all", + help="Controls how captured stdout/stderr/log is shown on failed tests. " + "Default: all.", + ) + group.addoption( + "--fulltrace", + "--full-trace", + action="store_true", + default=False, + help="Don't cut any tracebacks (default is to cut)", + ) + group.addoption( + "--color", + metavar="color", + action="store", + dest="color", + default="auto", + choices=["yes", "no", "auto"], + help="Color terminal output (yes/no/auto)", + ) + group.addoption( + "--code-highlight", + default="yes", + choices=["yes", "no"], + help="Whether code should be highlighted (only if --color is also enabled). " + "Default: yes.", + ) + + parser.addini( + "console_output_style", + help='Console output: "classic", or with additional progress information ' + '("progress" (percentage) | "count" | "progress-even-when-capture-no" (forces ' + "progress even when capture=no)", + default="progress", + ) + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_TEST_CASES, + help=( + "Specify a verbosity level for test case execution, overriding the main level. " + "Higher levels will provide more detailed information about each test case executed." + ), + ) + + +def pytest_configure(config: Config) -> None: + reporter = TerminalReporter(config, sys.stdout) + config.pluginmanager.register(reporter, "terminalreporter") + if config.option.debug or config.option.traceconfig: + + def mywriter(tags, args): + msg = " ".join(map(str, args)) + reporter.write_line("[traceconfig] " + msg) + + config.trace.root.setprocessor("pytest:config", mywriter) + + +def getreportopt(config: Config) -> str: + reportchars: str = config.option.reportchars + + old_aliases = {"F", "S"} + reportopts = "" + for char in reportchars: + if char in old_aliases: + char = char.lower() + if char == "a": + reportopts = "sxXEf" + elif char == "A": + reportopts = "PpsxXEf" + elif char == "N": + reportopts = "" + elif char not in reportopts: + reportopts += char + + if not config.option.disable_warnings and "w" not in reportopts: + reportopts = "w" + reportopts + elif config.option.disable_warnings and "w" in reportopts: + reportopts = reportopts.replace("w", "") + + return reportopts + + +@hookimpl(trylast=True) # after _pytest.runner +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str]: + letter = "F" + if report.passed: + letter = "." + elif report.skipped: + letter = "s" + + outcome: str = report.outcome + if report.when in ("collect", "setup", "teardown") and outcome == "failed": + outcome = "error" + letter = "E" + + return outcome, letter, outcome.upper() + + +@dataclasses.dataclass +class WarningReport: + """Simple structure to hold warnings information captured by ``pytest_warning_recorded``. + + :ivar str message: + User friendly message about the warning. + :ivar str|None nodeid: + nodeid that generated the warning (see ``get_location``). + :ivar tuple fslocation: + File system location of the source of the warning (see ``get_location``). + """ + + message: str + nodeid: str | None = None + fslocation: tuple[str, int] | None = None + + count_towards_summary: ClassVar = True + + def get_location(self, config: Config) -> str | None: + """Return the more user-friendly information about the location of a warning, or None.""" + if self.nodeid: + return self.nodeid + if self.fslocation: + filename, linenum = self.fslocation + relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) + return f"{relpath}:{linenum}" + return None + + +@final +class TerminalReporter: + def __init__(self, config: Config, file: TextIO | None = None) -> None: + import _pytest.config + + self.config = config + self._numcollected = 0 + self._session: Session | None = None + self._showfspath: bool | None = None + + self.stats: dict[str, list[Any]] = {} + self._main_color: str | None = None + self._known_types: list[str] | None = None + self.startpath = config.invocation_params.dir + if file is None: + file = sys.stdout + self._tw = _pytest.config.create_terminal_writer(config, file) + self._screen_width = self._tw.fullwidth + self.currentfspath: None | Path | str | int = None + self.reportchars = getreportopt(config) + self.foldskipped = config.option.fold_skipped + self.hasmarkup = self._tw.hasmarkup + # isatty should be a method but was wrongly implemented as a boolean. + # We use CallableBool here to support both. + self.isatty = compat.CallableBool(file.isatty()) + self._progress_nodeids_reported: set[str] = set() + self._timing_nodeids_reported: set[str] = set() + self._show_progress_info = self._determine_show_progress_info() + self._collect_report_last_write = timing.Instant() + self._already_displayed_warnings: int | None = None + self._keyboardinterrupt_memo: ExceptionRepr | None = None + + def _determine_show_progress_info( + self, + ) -> Literal["progress", "count", "times", False]: + """Return whether we should display progress information based on the current config.""" + # do not show progress if we are not capturing output (#3038) unless explicitly + # overridden by progress-even-when-capture-no + if ( + self.config.getoption("capture", "no") == "no" + and self.config.getini("console_output_style") + != "progress-even-when-capture-no" + ): + return False + # do not show progress if we are showing fixture setup/teardown + if self.config.getoption("setupshow", False): + return False + cfg: str = self.config.getini("console_output_style") + if cfg in {"progress", "progress-even-when-capture-no"}: + return "progress" + elif cfg == "count": + return "count" + elif cfg == "times": + return "times" + else: + return False + + @property + def verbosity(self) -> int: + verbosity: int = self.config.option.verbose + return verbosity + + @property + def showheader(self) -> bool: + return self.verbosity >= 0 + + @property + def no_header(self) -> bool: + return bool(self.config.option.no_header) + + @property + def no_summary(self) -> bool: + return bool(self.config.option.no_summary) + + @property + def showfspath(self) -> bool: + if self._showfspath is None: + return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) >= 0 + return self._showfspath + + @showfspath.setter + def showfspath(self, value: bool | None) -> None: + self._showfspath = value + + @property + def showlongtestinfo(self) -> bool: + return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) > 0 + + def hasopt(self, char: str) -> bool: + char = {"xfailed": "x", "skipped": "s"}.get(char, char) + return char in self.reportchars + + def write_fspath_result(self, nodeid: str, res: str, **markup: bool) -> None: + fspath = self.config.rootpath / nodeid.split("::")[0] + if self.currentfspath is None or fspath != self.currentfspath: + if self.currentfspath is not None and self._show_progress_info: + self._write_progress_information_filling_space() + self.currentfspath = fspath + relfspath = bestrelpath(self.startpath, fspath) + self._tw.line() + self._tw.write(relfspath + " ") + self._tw.write(res, flush=True, **markup) + + def write_ensure_prefix(self, prefix: str, extra: str = "", **kwargs) -> None: + if self.currentfspath != prefix: + self._tw.line() + self.currentfspath = prefix + self._tw.write(prefix) + if extra: + self._tw.write(extra, **kwargs) + self.currentfspath = -2 + + def ensure_newline(self) -> None: + if self.currentfspath: + self._tw.line() + self.currentfspath = None + + def wrap_write( + self, + content: str, + *, + flush: bool = False, + margin: int = 8, + line_sep: str = "\n", + **markup: bool, + ) -> None: + """Wrap message with margin for progress info.""" + width_of_current_line = self._tw.width_of_current_line + wrapped = line_sep.join( + textwrap.wrap( + " " * width_of_current_line + content, + width=self._screen_width - margin, + drop_whitespace=True, + replace_whitespace=False, + ), + ) + wrapped = wrapped[width_of_current_line:] + self._tw.write(wrapped, flush=flush, **markup) + + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + self._tw.write(content, flush=flush, **markup) + + def flush(self) -> None: + self._tw.flush() + + def write_line(self, line: str | bytes, **markup: bool) -> None: + if not isinstance(line, str): + line = str(line, errors="replace") + self.ensure_newline() + self._tw.line(line, **markup) + + def rewrite(self, line: str, **markup: bool) -> None: + """Rewinds the terminal cursor to the beginning and writes the given line. + + :param erase: + If True, will also add spaces until the full terminal width to ensure + previous lines are properly erased. + + The rest of the keyword arguments are markup instructions. + """ + erase = markup.pop("erase", False) + if erase: + fill_count = self._tw.fullwidth - len(line) - 1 + fill = " " * fill_count + else: + fill = "" + line = str(line) + self._tw.write("\r" + line + fill, **markup) + + def write_sep( + self, + sep: str, + title: str | None = None, + fullwidth: int | None = None, + **markup: bool, + ) -> None: + self.ensure_newline() + self._tw.sep(sep, title, fullwidth, **markup) + + def section(self, title: str, sep: str = "=", **kw: bool) -> None: + self._tw.sep(sep, title, **kw) + + def line(self, msg: str, **kw: bool) -> None: + self._tw.line(msg, **kw) + + def _add_stats(self, category: str, items: Sequence[Any]) -> None: + set_main_color = category not in self.stats + self.stats.setdefault(category, []).extend(items) + if set_main_color: + self._set_main_color() + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: + for line in str(excrepr).split("\n"): + self.write_line("INTERNALERROR> " + line) + return True + + def pytest_warning_recorded( + self, + warning_message: warnings.WarningMessage, + nodeid: str, + ) -> None: + from _pytest.warnings import warning_record_to_str + + fslocation = warning_message.filename, warning_message.lineno + message = warning_record_to_str(warning_message) + + warning_report = WarningReport( + fslocation=fslocation, message=message, nodeid=nodeid + ) + self._add_stats("warnings", [warning_report]) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: + if self.config.option.traceconfig: + msg = f"PLUGIN registered: {plugin}" + # XXX This event may happen during setup/teardown time + # which unfortunately captures our output here + # which garbles our output if we use self.write_line. + self.write_line(msg) + + def pytest_deselected(self, items: Sequence[Item]) -> None: + self._add_stats("deselected", items) + + def pytest_runtest_logstart( + self, nodeid: str, location: tuple[str, int | None, str] + ) -> None: + fspath, lineno, domain = location + # Ensure that the path is printed before the + # 1st test of a module starts running. + if self.showlongtestinfo: + line = self._locationline(nodeid, fspath, lineno, domain) + self.write_ensure_prefix(line, "") + self.flush() + elif self.showfspath: + self.write_fspath_result(nodeid, "") + self.flush() + + def pytest_runtest_logreport(self, report: TestReport) -> None: + self._tests_ran = True + rep = report + + res = TestShortLogReport( + *self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + ) + category, letter, word = res.category, res.letter, res.word + if not isinstance(word, tuple): + markup = None + else: + word, markup = word + self._add_stats(category, [rep]) + if not letter and not word: + # Probably passed setup/teardown. + return + if markup is None: + was_xfail = hasattr(report, "wasxfail") + if rep.passed and not was_xfail: + markup = {"green": True} + elif rep.passed and was_xfail: + markup = {"yellow": True} + elif rep.failed: + markup = {"red": True} + elif rep.skipped: + markup = {"yellow": True} + else: + markup = {} + self._progress_nodeids_reported.add(rep.nodeid) + if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0: + self._tw.write(letter, **markup) + # When running in xdist, the logreport and logfinish of multiple + # items are interspersed, e.g. `logreport`, `logreport`, + # `logfinish`, `logfinish`. To avoid the "past edge" calculation + # from getting confused and overflowing (#7166), do the past edge + # printing here and not in logfinish, except for the 100% which + # should only be printed after all teardowns are finished. + if self._show_progress_info and not self._is_last_item: + self._write_progress_information_if_past_edge() + else: + line = self._locationline(rep.nodeid, *rep.location) + running_xdist = hasattr(rep, "node") + if not running_xdist: + self.write_ensure_prefix(line, word, **markup) + if rep.skipped or hasattr(report, "wasxfail"): + reason = _get_raw_skip_reason(rep) + if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) < 2: + available_width = ( + (self._tw.fullwidth - self._tw.width_of_current_line) + - len(" [100%]") + - 1 + ) + formatted_reason = _format_trimmed( + " ({})", reason, available_width + ) + else: + formatted_reason = f" ({reason})" + + if reason and formatted_reason is not None: + self.wrap_write(formatted_reason) + if self._show_progress_info: + self._write_progress_information_filling_space() + else: + self.ensure_newline() + self._tw.write(f"[{rep.node.gateway.id}]") + if self._show_progress_info: + self._tw.write( + self._get_progress_information_message() + " ", cyan=True + ) + else: + self._tw.write(" ") + self._tw.write(word, **markup) + self._tw.write(" " + line) + self.currentfspath = -2 + self.flush() + + @property + def _is_last_item(self) -> bool: + assert self._session is not None + return len(self._progress_nodeids_reported) == self._session.testscollected + + @hookimpl(wrapper=True) + def pytest_runtestloop(self) -> Generator[None, object, object]: + result = yield + + # Write the final/100% progress -- deferred until the loop is complete. + if ( + self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 + and self._show_progress_info + and self._progress_nodeids_reported + ): + self._write_progress_information_filling_space() + + return result + + def _get_progress_information_message(self) -> str: + assert self._session + collected = self._session.testscollected + if self._show_progress_info == "count": + if collected: + progress = len(self._progress_nodeids_reported) + counter_format = f"{{:{len(str(collected))}d}}" + format_string = f" [{counter_format}/{{}}]" + return format_string.format(progress, collected) + return f" [ {collected} / {collected} ]" + if self._show_progress_info == "times": + if not collected: + return "" + all_reports = ( + self._get_reports_to_display("passed") + + self._get_reports_to_display("xpassed") + + self._get_reports_to_display("failed") + + self._get_reports_to_display("xfailed") + + self._get_reports_to_display("skipped") + + self._get_reports_to_display("error") + + self._get_reports_to_display("") + ) + current_location = all_reports[-1].location[0] + not_reported = [ + r for r in all_reports if r.nodeid not in self._timing_nodeids_reported + ] + tests_in_module = sum( + i.location[0] == current_location for i in self._session.items + ) + tests_completed = sum( + r.when == "setup" + for r in not_reported + if r.location[0] == current_location + ) + last_in_module = tests_completed == tests_in_module + if self.showlongtestinfo or last_in_module: + self._timing_nodeids_reported.update(r.nodeid for r in not_reported) + return format_node_duration( + sum(r.duration for r in not_reported if isinstance(r, TestReport)) + ) + return "" + if collected: + return f" [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]" + return " [100%]" + + def _write_progress_information_if_past_edge(self) -> None: + w = self._width_of_current_line + if self._show_progress_info == "count": + assert self._session + num_tests = self._session.testscollected + progress_length = len(f" [{num_tests}/{num_tests}]") + elif self._show_progress_info == "times": + progress_length = len(" 99h 59m") + else: + progress_length = len(" [100%]") + past_edge = w + progress_length + 1 >= self._screen_width + if past_edge: + main_color, _ = self._get_main_color() + msg = self._get_progress_information_message() + self._tw.write(msg + "\n", **{main_color: True}) + + def _write_progress_information_filling_space(self) -> None: + color, _ = self._get_main_color() + msg = self._get_progress_information_message() + w = self._width_of_current_line + fill = self._tw.fullwidth - w - 1 + self.write(msg.rjust(fill), flush=True, **{color: True}) + + @property + def _width_of_current_line(self) -> int: + """Return the width of the current line.""" + return self._tw.width_of_current_line + + def pytest_collection(self) -> None: + if self.isatty(): + if self.config.option.verbose >= 0: + self.write("collecting ... ", flush=True, bold=True) + elif self.config.option.verbose >= 1: + self.write("collecting ... ", flush=True, bold=True) + + def pytest_collectreport(self, report: CollectReport) -> None: + if report.failed: + self._add_stats("error", [report]) + elif report.skipped: + self._add_stats("skipped", [report]) + items = [x for x in report.result if isinstance(x, Item)] + self._numcollected += len(items) + if self.isatty(): + self.report_collect() + + def report_collect(self, final: bool = False) -> None: + if self.config.option.verbose < 0: + return + + if not final: + # Only write the "collecting" report every `REPORT_COLLECTING_RESOLUTION`. + if ( + self._collect_report_last_write.elapsed().seconds + < REPORT_COLLECTING_RESOLUTION + ): + return + self._collect_report_last_write = timing.Instant() + + errors = len(self.stats.get("error", [])) + skipped = len(self.stats.get("skipped", [])) + deselected = len(self.stats.get("deselected", [])) + selected = self._numcollected - deselected + line = "collected " if final else "collecting " + line += ( + str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") + ) + if errors: + line += f" / {errors} error{'s' if errors != 1 else ''}" + if deselected: + line += f" / {deselected} deselected" + if skipped: + line += f" / {skipped} skipped" + if self._numcollected > selected: + line += f" / {selected} selected" + if self.isatty(): + self.rewrite(line, bold=True, erase=True) + if final: + self.write("\n") + else: + self.write_line(line) + + @hookimpl(trylast=True) + def pytest_sessionstart(self, session: Session) -> None: + self._session = session + self._session_start = timing.Instant() + if not self.showheader: + return + self.write_sep("=", "test session starts", bold=True) + verinfo = platform.python_version() + if not self.no_header: + msg = f"platform {sys.platform} -- Python {verinfo}" + pypy_version_info = getattr(sys, "pypy_version_info", None) + if pypy_version_info: + verinfo = ".".join(map(str, pypy_version_info[:3])) + msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" + msg += f", pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}" + if ( + self.verbosity > 0 + or self.config.option.debug + or getattr(self.config.option, "pastebin", None) + ): + msg += " -- " + str(sys.executable) + self.write_line(msg) + lines = self.config.hook.pytest_report_header( + config=self.config, start_path=self.startpath + ) + self._write_report_lines_from_hooks(lines) + + def _write_report_lines_from_hooks( + self, lines: Sequence[str | Sequence[str]] + ) -> None: + for line_or_lines in reversed(lines): + if isinstance(line_or_lines, str): + self.write_line(line_or_lines) + else: + for line in line_or_lines: + self.write_line(line) + + def pytest_report_header(self, config: Config) -> list[str]: + result = [f"rootdir: {config.rootpath}"] + + if config.inipath: + result.append("configfile: " + bestrelpath(config.rootpath, config.inipath)) + + if config.args_source == Config.ArgsSource.TESTPATHS: + testpaths: list[str] = config.getini("testpaths") + result.append("testpaths: {}".format(", ".join(testpaths))) + + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + result.append( + "plugins: {}".format(", ".join(_plugin_nameversions(plugininfo))) + ) + return result + + def pytest_collection_finish(self, session: Session) -> None: + self.report_collect(True) + + lines = self.config.hook.pytest_report_collectionfinish( + config=self.config, + start_path=self.startpath, + items=session.items, + ) + self._write_report_lines_from_hooks(lines) + + if self.config.getoption("collectonly"): + if session.items: + if self.config.option.verbose > -1: + self._tw.line("") + self._printcollecteditems(session.items) + + failed = self.stats.get("failed") + if failed: + self._tw.sep("!", "collection failures") + for rep in failed: + rep.toterminal(self._tw) + + def _printcollecteditems(self, items: Sequence[Item]) -> None: + test_cases_verbosity = self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) + if test_cases_verbosity < 0: + if test_cases_verbosity < -1: + counts = Counter(item.nodeid.split("::", 1)[0] for item in items) + for name, count in sorted(counts.items()): + self._tw.line(f"{name}: {count}") + else: + for item in items: + self._tw.line(item.nodeid) + return + stack: list[Node] = [] + indent = "" + for item in items: + needed_collectors = item.listchain()[1:] # strip root node + while stack: + if stack == needed_collectors[: len(stack)]: + break + stack.pop() + for col in needed_collectors[len(stack) :]: + stack.append(col) + indent = (len(stack) - 1) * " " + self._tw.line(f"{indent}{col}") + if test_cases_verbosity >= 1: + obj = getattr(col, "obj", None) + doc = inspect.getdoc(obj) if obj else None + if doc: + for line in doc.splitlines(): + self._tw.line("{}{}".format(indent + " ", line)) + + @hookimpl(wrapper=True) + def pytest_sessionfinish( + self, session: Session, exitstatus: int | ExitCode + ) -> Generator[None]: + result = yield + self._tw.line("") + summary_exit_codes = ( + ExitCode.OK, + ExitCode.TESTS_FAILED, + ExitCode.INTERRUPTED, + ExitCode.USAGE_ERROR, + ExitCode.NO_TESTS_COLLECTED, + ) + if exitstatus in summary_exit_codes and not self.no_summary: + self.config.hook.pytest_terminal_summary( + terminalreporter=self, exitstatus=exitstatus, config=self.config + ) + if session.shouldfail: + self.write_sep("!", str(session.shouldfail), red=True) + if exitstatus == ExitCode.INTERRUPTED: + self._report_keyboardinterrupt() + self._keyboardinterrupt_memo = None + elif session.shouldstop: + self.write_sep("!", str(session.shouldstop), red=True) + self.summary_stats() + return result + + @hookimpl(wrapper=True) + def pytest_terminal_summary(self) -> Generator[None]: + self.summary_errors() + self.summary_failures() + self.summary_xfailures() + self.summary_warnings() + self.summary_passes() + self.summary_xpasses() + try: + return (yield) + finally: + self.short_test_summary() + # Display any extra warnings from teardown here (if any). + self.summary_warnings() + + def pytest_keyboard_interrupt(self, excinfo: ExceptionInfo[BaseException]) -> None: + self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) + + def pytest_unconfigure(self) -> None: + if self._keyboardinterrupt_memo is not None: + self._report_keyboardinterrupt() + + def _report_keyboardinterrupt(self) -> None: + excrepr = self._keyboardinterrupt_memo + assert excrepr is not None + assert excrepr.reprcrash is not None + msg = excrepr.reprcrash.message + self.write_sep("!", msg) + if "KeyboardInterrupt" in msg: + if self.config.option.fulltrace: + excrepr.toterminal(self._tw) + else: + excrepr.reprcrash.toterminal(self._tw) + self._tw.line( + "(to show a full traceback on KeyboardInterrupt use --full-trace)", + yellow=True, + ) + + def _locationline( + self, nodeid: str, fspath: str, lineno: int | None, domain: str + ) -> str: + def mkrel(nodeid: str) -> str: + line = self.config.cwd_relative_nodeid(nodeid) + if domain and line.endswith(domain): + line = line[: -len(domain)] + values = domain.split("[") + values[0] = values[0].replace(".", "::") # don't replace '.' in params + line += "[".join(values) + return line + + # fspath comes from testid which has a "/"-normalized path. + if fspath: + res = mkrel(nodeid) + if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( + "\\", nodes.SEP + ): + res += " <- " + bestrelpath(self.startpath, Path(fspath)) + else: + res = "[location]" + return res + " " + + def _getfailureheadline(self, rep): + head_line = rep.head_line + if head_line: + return head_line + return "test session" # XXX? + + def _getcrashline(self, rep): + try: + return str(rep.longrepr.reprcrash) + except AttributeError: + try: + return str(rep.longrepr)[:50] + except AttributeError: + return "" + + # + # Summaries for sessionfinish. + # + def getreports(self, name: str): + return [x for x in self.stats.get(name, ()) if not hasattr(x, "_pdbshown")] + + def summary_warnings(self) -> None: + if self.hasopt("w"): + all_warnings: list[WarningReport] | None = self.stats.get("warnings") + if not all_warnings: + return + + final = self._already_displayed_warnings is not None + if final: + warning_reports = all_warnings[self._already_displayed_warnings :] + else: + warning_reports = all_warnings + self._already_displayed_warnings = len(warning_reports) + if not warning_reports: + return + + reports_grouped_by_message: dict[str, list[WarningReport]] = {} + for wr in warning_reports: + reports_grouped_by_message.setdefault(wr.message, []).append(wr) + + def collapsed_location_report(reports: list[WarningReport]) -> str: + locations = [] + for w in reports: + location = w.get_location(self.config) + if location: + locations.append(location) + + if len(locations) < 10: + return "\n".join(map(str, locations)) + + counts_by_filename = Counter( + str(loc).split("::", 1)[0] for loc in locations + ) + return "\n".join( + "{}: {} warning{}".format(k, v, "s" if v > 1 else "") + for k, v in counts_by_filename.items() + ) + + title = "warnings summary (final)" if final else "warnings summary" + self.write_sep("=", title, yellow=True, bold=False) + for message, message_reports in reports_grouped_by_message.items(): + maybe_location = collapsed_location_report(message_reports) + if maybe_location: + self._tw.line(maybe_location) + lines = message.splitlines() + indented = "\n".join(" " + x for x in lines) + message = indented.rstrip() + else: + message = message.rstrip() + self._tw.line(message) + self._tw.line() + self._tw.line( + "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html" + ) + + def summary_passes(self) -> None: + self.summary_passes_combined("passed", "PASSES", "P") + + def summary_xpasses(self) -> None: + self.summary_passes_combined("xpassed", "XPASSES", "X") + + def summary_passes_combined( + self, which_reports: str, sep_title: str, needed_opt: str + ) -> None: + if self.config.option.tbstyle != "no": + if self.hasopt(needed_opt): + reports: list[TestReport] = self.getreports(which_reports) + if not reports: + return + self.write_sep("=", sep_title) + for rep in reports: + if rep.sections: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, green=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def _get_teardown_reports(self, nodeid: str) -> list[TestReport]: + reports = self.getreports("") + return [ + report + for report in reports + if report.when == "teardown" and report.nodeid == nodeid + ] + + def _handle_teardown_sections(self, nodeid: str) -> None: + for report in self._get_teardown_reports(nodeid): + self.print_teardown_sections(report) + + def print_teardown_sections(self, rep: TestReport) -> None: + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + if "teardown" in secname: + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_failures(self) -> None: + style = self.config.option.tbstyle + self.summary_failures_combined("failed", "FAILURES", style=style) + + def summary_xfailures(self) -> None: + show_tb = self.config.option.xfail_tb + style = self.config.option.tbstyle if show_tb else "no" + self.summary_failures_combined("xfailed", "XFAILURES", style=style) + + def summary_failures_combined( + self, + which_reports: str, + sep_title: str, + *, + style: str, + needed_opt: str | None = None, + ) -> None: + if style != "no": + if not needed_opt or self.hasopt(needed_opt): + reports: list[BaseReport] = self.getreports(which_reports) + if not reports: + return + self.write_sep("=", sep_title) + if style == "line": + for rep in reports: + line = self._getcrashline(rep) + self.write_line(line) + else: + for rep in reports: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def summary_errors(self) -> None: + if self.config.option.tbstyle != "no": + reports: list[BaseReport] = self.getreports("error") + if not reports: + return + self.write_sep("=", "ERRORS") + for rep in self.stats["error"]: + msg = self._getfailureheadline(rep) + if rep.when == "collect": + msg = "ERROR collecting " + msg + else: + msg = f"ERROR at {rep.when} of {msg}" + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + + def _outrep_summary(self, rep: BaseReport) -> None: + rep.toterminal(self._tw) + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_stats(self) -> None: + if self.verbosity < -1: + return + + session_duration = self._session_start.elapsed() + (parts, main_color) = self.build_summary_stats_line() + line_parts = [] + + display_sep = self.verbosity >= 0 + if display_sep: + fullwidth = self._tw.fullwidth + for text, markup in parts: + with_markup = self._tw.markup(text, **markup) + if display_sep: + fullwidth += len(with_markup) - len(text) + line_parts.append(with_markup) + msg = ", ".join(line_parts) + + main_markup = {main_color: True} + duration = f" in {format_session_duration(session_duration.seconds)}" + duration_with_markup = self._tw.markup(duration, **main_markup) + if display_sep: + fullwidth += len(duration_with_markup) - len(duration) + msg += duration_with_markup + + if display_sep: + markup_for_end_sep = self._tw.markup("", **main_markup) + if markup_for_end_sep.endswith("\x1b[0m"): + markup_for_end_sep = markup_for_end_sep[:-4] + fullwidth += len(markup_for_end_sep) + msg += markup_for_end_sep + + if display_sep: + self.write_sep("=", msg, fullwidth=fullwidth, **main_markup) + else: + self.write_line(msg, **main_markup) + + def short_test_summary(self) -> None: + if not self.reportchars: + return + + def show_simple(lines: list[str], *, stat: str) -> None: + failed = self.stats.get(stat, []) + if not failed: + return + config = self.config + for rep in failed: + color = _color_for_type.get(stat, _color_for_type_default) + line = _get_line_with_reprcrash_message( + config, rep, self._tw, {color: True} + ) + lines.append(line) + + def show_xfailed(lines: list[str]) -> None: + xfailed = self.stats.get("xfailed", []) + for rep in xfailed: + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.wasxfail + if reason: + line += " - " + str(reason) + + lines.append(line) + + def show_xpassed(lines: list[str]) -> None: + xpassed = self.stats.get("xpassed", []) + for rep in xpassed: + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.wasxfail + if reason: + line += " - " + str(reason) + lines.append(line) + + def show_skipped_folded(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get("skipped", []) + fskips = _folded_skips(self.startpath, skipped) if skipped else [] + if not fskips: + return + verbose_word, verbose_markup = skipped[0]._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + prefix = "Skipped: " + for num, fspath, lineno, reason in fskips: + if reason.startswith(prefix): + reason = reason[len(prefix) :] + if lineno is not None: + lines.append(f"{markup_word} [{num}] {fspath}:{lineno}: {reason}") + else: + lines.append(f"{markup_word} [{num}] {fspath}: {reason}") + + def show_skipped_unfolded(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get("skipped", []) + + for rep in skipped: + assert rep.longrepr is not None + assert isinstance(rep.longrepr, tuple), (rep, rep.longrepr) + assert len(rep.longrepr) == 3, (rep, rep.longrepr) + + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.longrepr[2] + if reason: + line += " - " + str(reason) + lines.append(line) + + def show_skipped(lines: list[str]) -> None: + if self.foldskipped: + show_skipped_folded(lines) + else: + show_skipped_unfolded(lines) + + REPORTCHAR_ACTIONS: Mapping[str, Callable[[list[str]], None]] = { + "x": show_xfailed, + "X": show_xpassed, + "f": partial(show_simple, stat="failed"), + "s": show_skipped, + "p": partial(show_simple, stat="passed"), + "E": partial(show_simple, stat="error"), + } + + lines: list[str] = [] + for char in self.reportchars: + action = REPORTCHAR_ACTIONS.get(char) + if action: # skipping e.g. "P" (passed with output) here. + action(lines) + + if lines: + self.write_sep("=", "short test summary info", cyan=True, bold=True) + for line in lines: + self.write_line(line) + + def _get_main_color(self) -> tuple[str, list[str]]: + if self._main_color is None or self._known_types is None or self._is_last_item: + self._set_main_color() + assert self._main_color + assert self._known_types + return self._main_color, self._known_types + + def _determine_main_color(self, unknown_type_seen: bool) -> str: + stats = self.stats + if "failed" in stats or "error" in stats: + main_color = "red" + elif "warnings" in stats or "xpassed" in stats or unknown_type_seen: + main_color = "yellow" + elif "passed" in stats or not self._is_last_item: + main_color = "green" + else: + main_color = "yellow" + return main_color + + def _set_main_color(self) -> None: + unknown_types: list[str] = [] + for found_type in self.stats: + if found_type: # setup/teardown reports have an empty key, ignore them + if found_type not in KNOWN_TYPES and found_type not in unknown_types: + unknown_types.append(found_type) + self._known_types = list(KNOWN_TYPES) + unknown_types + self._main_color = self._determine_main_color(bool(unknown_types)) + + def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: + """ + Build the parts used in the last summary stats line. + + The summary stats line is the line shown at the end, "=== 12 passed, 2 errors in Xs===". + + This function builds a list of the "parts" that make up for the text in that line, in + the example above it would be:: + + [ + ("12 passed", {"green": True}), + ("2 errors", {"red": True} + ] + + That last dict for each line is a "markup dictionary", used by TerminalWriter to + color output. + + The final color of the line is also determined by this function, and is the second + element of the returned tuple. + """ + if self.config.getoption("collectonly"): + return self._build_collect_only_summary_stats_line() + else: + return self._build_normal_summary_stats_line() + + def _get_reports_to_display(self, key: str) -> list[Any]: + """Get test/collection reports for the given status key, such as `passed` or `error`.""" + reports = self.stats.get(key, []) + return [x for x in reports if getattr(x, "count_towards_summary", True)] + + def _build_normal_summary_stats_line( + self, + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: + main_color, known_types = self._get_main_color() + parts = [] + + for key in known_types: + reports = self._get_reports_to_display(key) + if reports: + count = len(reports) + color = _color_for_type.get(key, _color_for_type_default) + markup = {color: True, "bold": color == main_color} + parts.append(("%d %s" % pluralize(count, key), markup)) # noqa: UP031 + + if not parts: + parts = [("no tests ran", {_color_for_type_default: True})] + + return parts, main_color + + def _build_collect_only_summary_stats_line( + self, + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: + deselected = len(self._get_reports_to_display("deselected")) + errors = len(self._get_reports_to_display("error")) + + if self._numcollected == 0: + parts = [("no tests collected", {"yellow": True})] + main_color = "yellow" + + elif deselected == 0: + main_color = "green" + collected_output = "%d %s collected" % pluralize(self._numcollected, "test") # noqa: UP031 + parts = [(collected_output, {main_color: True})] + else: + all_tests_were_deselected = self._numcollected == deselected + if all_tests_were_deselected: + main_color = "yellow" + collected_output = f"no tests collected ({deselected} deselected)" + else: + main_color = "green" + selected = self._numcollected - deselected + collected_output = f"{selected}/{self._numcollected} tests collected ({deselected} deselected)" + + parts = [(collected_output, {main_color: True})] + + if errors: + main_color = _color_for_type["error"] + parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] # noqa: UP031 + + return parts, main_color + + +def _get_node_id_with_markup(tw: TerminalWriter, config: Config, rep: BaseReport): + nodeid = config.cwd_relative_nodeid(rep.nodeid) + path, *parts = nodeid.split("::") + if parts: + parts_markup = tw.markup("::".join(parts), bold=True) + return path + "::" + parts_markup + else: + return path + + +def _format_trimmed(format: str, msg: str, available_width: int) -> str | None: + """Format msg into format, ellipsizing it if doesn't fit in available_width. + + Returns None if even the ellipsis can't fit. + """ + # Only use the first line. + i = msg.find("\n") + if i != -1: + msg = msg[:i] + + ellipsis = "..." + format_width = wcswidth(format.format("")) + if format_width + len(ellipsis) > available_width: + return None + + if format_width + wcswidth(msg) > available_width: + available_width -= len(ellipsis) + msg = msg[:available_width] + while format_width + wcswidth(msg) > available_width: + msg = msg[:-1] + msg += ellipsis + + return format.format(msg) + + +def _get_line_with_reprcrash_message( + config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: dict[str, bool] +) -> str: + """Get summary line for a report, trying to add reprcrash message.""" + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + config, word_markup + ) + word = tw.markup(verbose_word, **verbose_markup) + node = _get_node_id_with_markup(tw, config, rep) + + line = f"{word} {node}" + line_width = wcswidth(line) + + try: + # Type ignored intentionally -- possible AttributeError expected. + msg = rep.longrepr.reprcrash.message # type: ignore[union-attr] + except AttributeError: + pass + else: + if ( + running_on_ci() or config.option.verbose >= 2 + ) and not config.option.force_short_summary: + msg = f" - {msg}" + else: + available_width = tw.fullwidth - line_width + msg = _format_trimmed(" - {}", msg, available_width) + if msg is not None: + line += msg + + return line + + +def _folded_skips( + startpath: Path, + skipped: Sequence[CollectReport], +) -> list[tuple[int, str, int | None, str]]: + d: dict[tuple[str, int | None, str], list[CollectReport]] = {} + for event in skipped: + assert event.longrepr is not None + assert isinstance(event.longrepr, tuple), (event, event.longrepr) + assert len(event.longrepr) == 3, (event, event.longrepr) + fspath, lineno, reason = event.longrepr + # For consistency, report all fspaths in relative form. + fspath = bestrelpath(startpath, Path(fspath)) + keywords = getattr(event, "keywords", {}) + # Folding reports with global pytestmark variable. + # This is a workaround, because for now we cannot identify the scope of a skip marker + # TODO: Revisit after marks scope would be fixed. + if ( + event.when == "setup" + and "skip" in keywords + and "pytestmark" not in keywords + ): + key: tuple[str, int | None, str] = (fspath, None, reason) + else: + key = (fspath, lineno, reason) + d.setdefault(key, []).append(event) + values: list[tuple[int, str, int | None, str]] = [] + for key, events in d.items(): + values.append((len(events), *key)) + return values + + +_color_for_type = { + "failed": "red", + "error": "red", + "warnings": "yellow", + "passed": "green", +} +_color_for_type_default = "yellow" + + +def pluralize(count: int, noun: str) -> tuple[int, str]: + # No need to pluralize words such as `failed` or `passed`. + if noun not in ["error", "warnings", "test"]: + return count, noun + + # The `warnings` key is plural. To avoid API breakage, we keep it that way but + # set it to singular here so we can determine plurality in the same way as we do + # for `error`. + noun = noun.replace("warnings", "warning") + + return count, noun + "s" if count != 1 else noun + + +def _plugin_nameversions(plugininfo) -> list[str]: + values: list[str] = [] + for plugin, dist in plugininfo: + # Gets us name and version! + name = f"{dist.project_name}-{dist.version}" + # Questionable convenience, but it keeps things short. + if name.startswith("pytest-"): + name = name[7:] + # We decided to print python package names they can have more than one plugin. + if name not in values: + values.append(name) + return values + + +def format_session_duration(seconds: float) -> str: + """Format the given seconds in a human readable manner to show in the final summary.""" + if seconds < 60: + return f"{seconds:.2f}s" + else: + dt = datetime.timedelta(seconds=int(seconds)) + return f"{seconds:.2f}s ({dt})" + + +def format_node_duration(seconds: float) -> str: + """Format the given seconds in a human readable manner to show in the test progress.""" + # The formatting is designed to be compact and readable, with at most 7 characters + # for durations below 100 hours. + if seconds < 0.00001: + return f" {seconds * 1000000:.3f}us" + if seconds < 0.0001: + return f" {seconds * 1000000:.2f}us" + if seconds < 0.001: + return f" {seconds * 1000000:.1f}us" + if seconds < 0.01: + return f" {seconds * 1000:.3f}ms" + if seconds < 0.1: + return f" {seconds * 1000:.2f}ms" + if seconds < 1: + return f" {seconds * 1000:.1f}ms" + if seconds < 60: + return f" {seconds:.3f}s" + if seconds < 3600: + return f" {seconds // 60:.0f}m {seconds % 60:.0f}s" + return f" {seconds // 3600:.0f}h {(seconds % 3600) // 60:.0f}m" + + +def _get_raw_skip_reason(report: TestReport) -> str: + """Get the reason string of a skip/xfail/xpass test report. + + The string is just the part given by the user. + """ + if hasattr(report, "wasxfail"): + reason = report.wasxfail + if reason.startswith("reason: "): + reason = reason[len("reason: ") :] + return reason + else: + assert report.skipped + assert isinstance(report.longrepr, tuple) + _, _, reason = report.longrepr + if reason.startswith("Skipped: "): + reason = reason[len("Skipped: ") :] + elif reason == "Skipped": + reason = "" + return reason diff --git a/.venv/lib/python3.11/site-packages/_pytest/threadexception.py b/.venv/lib/python3.11/site-packages/_pytest/threadexception.py new file mode 100644 index 00000000..eb57783b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/threadexception.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import sys +import threading +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +class ThreadExceptionMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +thread_exceptions: StashKey[collections.deque[ThreadExceptionMeta | BaseException]] = ( + StashKey() +) + + +def collect_thread_exception(config: Config) -> None: + pop_thread_exception = config.stash[thread_exceptions].pop + errors: list[pytest.PytestUnhandledThreadExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_thread_exception() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process thread exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + except pytest.PytestUnhandledThreadExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple thread exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[threading.ExceptHookArgs], object] +) -> None: + try: + try: + # We don't join threads here, so exceptions raised from any + # threads still running by the time _threading_atexits joins them + # do not get captured (see #13027). + collect_thread_exception(config) + finally: + threading.excepthook = prev_hook + finally: + del config.stash[thread_exceptions] + + +def thread_exception_hook( + args: threading.ExceptHookArgs, + /, + *, + append: Callable[[ThreadExceptionMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the excepthook finishes and before the metadata object is + # collected by a pytest hook + thread_name = "" if args.thread is None else args.thread.name + summary = f"Exception in thread {thread_name}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + args.exc_type, + args.exc_value, + args.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(args.thread) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + ThreadExceptionMeta( + # Compute these strings here as they might change later + msg=msg, + cause_msg=cause_msg, + exc_value=args.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_thread_exception and once by sys.excepthook + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = threading.excepthook + deque: collections.deque[ThreadExceptionMeta | BaseException] = collections.deque() + config.stash[thread_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + threading.excepthook = functools.partial(thread_exception_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_thread_exception(item.config) diff --git a/.venv/lib/python3.11/site-packages/_pytest/timing.py b/.venv/lib/python3.11/site-packages/_pytest/timing.py new file mode 100644 index 00000000..221eeffc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/timing.py @@ -0,0 +1,94 @@ +"""Indirection for time functions. + +We intentionally grab some "time" functions internally to avoid tests mocking "time" to affect +pytest runtime information (issue #185). + +Fixture "mock_timing" also interacts with this module for pytest's own tests. +""" + +from __future__ import annotations + +import dataclasses +from datetime import datetime +from datetime import timezone +from time import perf_counter +from time import sleep +from time import time +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from pytest import MonkeyPatch + + +@dataclasses.dataclass(frozen=True) +class Instant: + """ + Represents an instant in time, used to both get the timestamp value and to measure + the duration of a time span. + + Inspired by Rust's `std::time::Instant`. + """ + + # Creation time of this instant, using time.time(), to measure actual time. + # Note: using a `lambda` to correctly get the mocked time via `MockTiming`. + time: float = dataclasses.field(default_factory=lambda: time(), init=False) + + # Performance counter tick of the instant, used to measure precise elapsed time. + # Note: using a `lambda` to correctly get the mocked time via `MockTiming`. + perf_count: float = dataclasses.field( + default_factory=lambda: perf_counter(), init=False + ) + + def elapsed(self) -> Duration: + """Measure the duration since `Instant` was created.""" + return Duration(start=self, stop=Instant()) + + def as_utc(self) -> datetime: + """Instant as UTC datetime.""" + return datetime.fromtimestamp(self.time, timezone.utc) + + +@dataclasses.dataclass(frozen=True) +class Duration: + """A span of time as measured by `Instant.elapsed()`.""" + + start: Instant + stop: Instant + + @property + def seconds(self) -> float: + """Elapsed time of the duration in seconds, measured using a performance counter for precise timing.""" + return self.stop.perf_count - self.start.perf_count + + +@dataclasses.dataclass +class MockTiming: + """Mocks _pytest.timing with a known object that can be used to control timing in tests + deterministically. + + pytest itself should always use functions from `_pytest.timing` instead of `time` directly. + + This then allows us more control over time during testing, if testing code also + uses `_pytest.timing` functions. + + Time is static, and only advances through `sleep` calls, thus tests might sleep over large + numbers and obtain accurate time() calls at the end, making tests reliable and instant.""" + + _current_time: float = datetime(2020, 5, 22, 14, 20, 50).timestamp() + + def sleep(self, seconds: float) -> None: + self._current_time += seconds + + def time(self) -> float: + return self._current_time + + def patch(self, monkeypatch: MonkeyPatch) -> None: + from _pytest import timing # noqa: PLW0406 + + monkeypatch.setattr(timing, "sleep", self.sleep) + monkeypatch.setattr(timing, "time", self.time) + monkeypatch.setattr(timing, "perf_counter", self.time) + + +__all__ = ["perf_counter", "sleep", "time"] diff --git a/.venv/lib/python3.11/site-packages/_pytest/tmpdir.py b/.venv/lib/python3.11/site-packages/_pytest/tmpdir.py new file mode 100644 index 00000000..dcd5784f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/tmpdir.py @@ -0,0 +1,312 @@ +# mypy: allow-untyped-defs +"""Support for providing temporary directories to test functions.""" + +from __future__ import annotations + +from collections.abc import Generator +import dataclasses +import os +from pathlib import Path +import re +from shutil import rmtree +import tempfile +from typing import Any +from typing import final +from typing import Literal + +from .pathlib import cleanup_dead_symlinks +from .pathlib import LOCK_TIMEOUT +from .pathlib import make_numbered_dir +from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf +from _pytest.compat import get_user_id +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Item +from _pytest.reports import TestReport +from _pytest.stash import StashKey + + +tmppath_result_key = StashKey[dict[str, bool]]() +RetentionType = Literal["all", "failed", "none"] + + +@final +@dataclasses.dataclass +class TempPathFactory: + """Factory for temporary directories under the common base temp directory, + as discussed at :ref:`temporary directory location and retention`. + """ + + _given_basetemp: Path | None + # pluggy TagTracerSub, not currently exposed, so Any. + _trace: Any + _basetemp: Path | None + _retention_count: int + _retention_policy: RetentionType + + def __init__( + self, + given_basetemp: Path | None, + retention_count: int, + retention_policy: RetentionType, + trace, + basetemp: Path | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + if given_basetemp is None: + self._given_basetemp = None + else: + # Use os.path.abspath() to get absolute path instead of resolve() as it + # does not work the same in all platforms (see #4427). + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). + self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) + self._trace = trace + self._retention_count = retention_count + self._retention_policy = retention_policy + self._basetemp = basetemp + + @classmethod + def from_config( + cls, + config: Config, + *, + _ispytest: bool = False, + ) -> TempPathFactory: + """Create a factory according to pytest configuration. + + :meta private: + """ + check_ispytest(_ispytest) + count = int(config.getini("tmp_path_retention_count")) + if count < 0: + raise ValueError( + f"tmp_path_retention_count must be >= 0. Current input: {count}." + ) + + policy = config.getini("tmp_path_retention_policy") + if policy not in ("all", "failed", "none"): + raise ValueError( + f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." + ) + + return cls( + given_basetemp=config.option.basetemp, + trace=config.trace.get("tmpdir"), + retention_count=count, + retention_policy=policy, + _ispytest=True, + ) + + def _ensure_relative_to_basetemp(self, basename: str) -> str: + basename = os.path.normpath(basename) + if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): + raise ValueError(f"{basename} is not a normalized and relative path") + return basename + + def mktemp(self, basename: str, numbered: bool = True) -> Path: + """Create a new temporary directory managed by the factory. + + :param basename: + Directory base name, must be a relative path. + + :param numbered: + If ``True``, ensure the directory is unique by adding a numbered + suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` + means that this function will create directories named ``"foo-0"``, + ``"foo-1"``, ``"foo-2"`` and so on. + + :returns: + The path to the new directory. + """ + basename = self._ensure_relative_to_basetemp(basename) + if not numbered: + p = self.getbasetemp().joinpath(basename) + p.mkdir(mode=0o700) + else: + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) + self._trace("mktemp", p) + return p + + def getbasetemp(self) -> Path: + """Return the base temporary directory, creating it if needed. + + :returns: + The base temporary directory. + """ + if self._basetemp is not None: + return self._basetemp + + if self._given_basetemp is not None: + basetemp = self._given_basetemp + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir(mode=0o700) + basetemp = basetemp.resolve() + else: + from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + temproot = Path(from_env or tempfile.gettempdir()).resolve() + user = get_user() or "unknown" + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.joinpath(f"pytest-of-{user}") + try: + rootdir.mkdir(mode=0o700, exist_ok=True) + except OSError: + # getuser() likely returned illegal characters for the platform, use unknown back off mechanism + rootdir = temproot.joinpath("pytest-of-unknown") + rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + uid = get_user_id() + if uid is not None: + rootdir_stat = rootdir.stat() + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) + keep = self._retention_count + if self._retention_policy == "none": + keep = 0 + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", + root=rootdir, + keep=keep, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, + ) + assert basetemp is not None, basetemp + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp + + +def get_user() -> str | None: + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010).""" + try: + # In some exotic environments, getpass may not be importable. + import getpass + + return getpass.getuser() + except (ImportError, OSError, KeyError): + return None + + +def pytest_configure(config: Config) -> None: + """Create a TempPathFactory and attach it to the config object. + + This is to comply with existing plugins which expect the handler to be + available at pytest_configure time, but ideally should be moved entirely + to the tmp_path_factory session fixture. + """ + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) + mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "tmp_path_retention_count", + help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", + default=3, + ) + + parser.addini( + "tmp_path_retention_policy", + help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " + "(all/failed/none)", + default="all", + ) + + +@fixture(scope="session") +def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: + """Return a :class:`pytest.TempPathFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmp_path_factory # type: ignore + + +def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: + name = request.node.name + name = re.sub(r"[\W]", "_", name) + MAXVAL = 30 + name = name[:MAXVAL] + return factory.mktemp(name, numbered=True) + + +@fixture +def tmp_path( + request: FixtureRequest, tmp_path_factory: TempPathFactory +) -> Generator[Path]: + """Return a temporary directory (as :class:`pathlib.Path` object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + """ + path = _mk_tmp(request, tmp_path_factory) + yield path + + # Remove the tmpdir if the policy is "failed" and the test passed. + policy = tmp_path_factory._retention_policy + result_dict = request.node.stash[tmppath_result_key] + + if policy == "failed" and result_dict.get("call", True): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(path, ignore_errors=True) + + del request.node.stash[tmppath_result_key] + + +def pytest_sessionfinish(session, exitstatus: int | ExitCode): + """After each session, remove base directory if all the tests passed, + the policy is "failed", and the basetemp is not specified by a user. + """ + tmp_path_factory: TempPathFactory = session.config._tmp_path_factory + basetemp = tmp_path_factory._basetemp + if basetemp is None: + return + + policy = tmp_path_factory._retention_policy + if ( + exitstatus == 0 + and policy == "failed" + and tmp_path_factory._given_basetemp is None + ): + if basetemp.is_dir(): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(basetemp, ignore_errors=True) + + # Remove dead symlinks. + if basetemp.is_dir(): + cleanup_dead_symlinks(basetemp) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_makereport( + item: Item, call +) -> Generator[None, TestReport, TestReport]: + rep = yield + assert rep.when is not None + empty: dict[str, bool] = {} + item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed + return rep diff --git a/.venv/lib/python3.11/site-packages/_pytest/tracemalloc.py b/.venv/lib/python3.11/site-packages/_pytest/tracemalloc.py new file mode 100644 index 00000000..5d0b1985 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/tracemalloc.py @@ -0,0 +1,24 @@ +from __future__ import annotations + + +def tracemalloc_message(source: object) -> str: + if source is None: + return "" + + try: + import tracemalloc + except ImportError: + return "" + + tb = tracemalloc.get_object_traceback(source) + if tb is not None: + formatted_tb = "\n".join(tb.format()) + # Use a leading new line to better separate the (large) output + # from the traceback to the previous warning text. + return f"\nObject allocated at:\n{formatted_tb}" + # No need for a leading new line. + url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" + return ( + "Enable tracemalloc to get traceback where the object was allocated.\n" + f"See {url} for more info." + ) diff --git a/.venv/lib/python3.11/site-packages/_pytest/unittest.py b/.venv/lib/python3.11/site-packages/_pytest/unittest.py new file mode 100644 index 00000000..ef6ef64a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/unittest.py @@ -0,0 +1,516 @@ +# mypy: allow-untyped-defs +"""Discover and run std-library "unittest" style tests.""" + +from __future__ import annotations + +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from enum import auto +from enum import Enum +import inspect +import sys +import traceback +import types +from typing import TYPE_CHECKING +from typing import Union + +import _pytest._code +from _pytest.compat import is_async_function +from _pytest.config import hookimpl +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import exit +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.python import Class +from _pytest.python import Function +from _pytest.python import Module +from _pytest.runner import CallInfo +import pytest + + +if sys.version_info[:2] < (3, 11): + from exceptiongroup import ExceptionGroup + +if TYPE_CHECKING: + import unittest + + import twisted.trial.unittest + + +_SysExcInfoType = Union[ + tuple[type[BaseException], BaseException, types.TracebackType], + tuple[None, None, None], +] + + +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> UnitTestCase | None: + try: + # Has unittest been imported? + ut = sys.modules["unittest"] + # Is obj a subclass of unittest.TestCase? + # Type ignored because `ut` is an opaque module. + if not issubclass(obj, ut.TestCase): # type: ignore + return None + except Exception: + return None + # Is obj a concrete class? + # Abstract classes can't be instantiated so no point collecting them. + if inspect.isabstract(obj): + return None + # Yes, so let's collect it. + return UnitTestCase.from_parent(collector, name=name, obj=obj) + + +class UnitTestCase(Class): + # Marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs. + nofuncargs = True + + def newinstance(self): + # TestCase __init__ takes the method (test) name. The TestCase + # constructor treats the name "runTest" as a special no-op, so it can be + # used when a dummy instance is needed. While unittest.TestCase has a + # default, some subclasses omit the default (#9610), so always supply + # it. + return self.obj("runTest") + + def collect(self) -> Iterable[Item | Collector]: + from unittest import TestLoader + + cls = self.obj + if not getattr(cls, "__test__", True): + return + + skipped = _is_skipped(cls) + if not skipped: + self._register_unittest_setup_method_fixture(cls) + self._register_unittest_setup_class_fixture(cls) + self._register_setup_class_fixture() + + self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) + + loader = TestLoader() + foundsomething = False + for name in loader.getTestCaseNames(self.obj): + x = getattr(self.obj, name) + if not getattr(x, "__test__", True): + continue + yield TestCaseFunction.from_parent(self, name=name) + foundsomething = True + + if not foundsomething: + runtest = getattr(self.obj, "runTest", None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + if ut is None or runtest != ut.TestCase.runTest: + yield TestCaseFunction.from_parent(self, name="runTest") + + def _register_unittest_setup_class_fixture(self, cls: type) -> None: + """Register an auto-use fixture to invoke setUpClass and + tearDownClass (#517).""" + setup = getattr(cls, "setUpClass", None) + teardown = getattr(cls, "tearDownClass", None) + if setup is None and teardown is None: + return None + cleanup = getattr(cls, "doClassCleanups", lambda: None) + + def process_teardown_exceptions() -> None: + # tearDown_exceptions is a list set in the class containing exc_infos for errors during + # teardown for the class. + exc_infos = getattr(cls, "tearDown_exceptions", None) + if not exc_infos: + return + exceptions = [exc for (_, exc, _) in exc_infos] + # If a single exception, raise it directly as this provides a more readable + # error (hopefully this will improve in #12255). + if len(exceptions) == 1: + raise exceptions[0] + else: + raise ExceptionGroup("Unittest class cleanup errors", exceptions) + + def unittest_setup_class_fixture( + request: FixtureRequest, + ) -> Generator[None]: + cls = request.cls + if _is_skipped(cls): + reason = cls.__unittest_skip_why__ + raise pytest.skip.Exception(reason, _use_item_location=True) + if setup is not None: + try: + setup() + # unittest does not call the cleanup function for every BaseException, so we + # follow this here. + except Exception: + cleanup() + process_teardown_exceptions() + raise + yield + try: + if teardown is not None: + teardown() + finally: + cleanup() + process_teardown_exceptions() + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_unittest_setUpClass_fixture_{cls.__qualname__}", + func=unittest_setup_class_fixture, + nodeid=self.nodeid, + scope="class", + autouse=True, + ) + + def _register_unittest_setup_method_fixture(self, cls: type) -> None: + """Register an auto-use fixture to invoke setup_method and + teardown_method (#517).""" + setup = getattr(cls, "setup_method", None) + teardown = getattr(cls, "teardown_method", None) + if setup is None and teardown is None: + return None + + def unittest_setup_method_fixture( + request: FixtureRequest, + ) -> Generator[None]: + self = request.instance + if _is_skipped(self): + reason = self.__unittest_skip_why__ + raise pytest.skip.Exception(reason, _use_item_location=True) + if setup is not None: + setup(self, request.function) + yield + if teardown is not None: + teardown(self, request.function) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_unittest_setup_method_fixture_{cls.__qualname__}", + func=unittest_setup_method_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +class TestCaseFunction(Function): + nofuncargs = True + _excinfo: list[_pytest._code.ExceptionInfo[BaseException]] | None = None + + def _getinstance(self): + assert isinstance(self.parent, UnitTestCase) + return self.parent.obj(self.name) + + # Backward compat for pytest-django; can be removed after pytest-django + # updates + some slack. + @property + def _testcase(self): + return self.instance + + def setup(self) -> None: + # A bound method to be called during teardown() if set (see 'runtest()'). + self._explicit_tearDown: Callable[[], None] | None = None + super().setup() + + def teardown(self) -> None: + if self._explicit_tearDown is not None: + self._explicit_tearDown() + self._explicit_tearDown = None + self._obj = None + del self._instance + super().teardown() + + def startTest(self, testcase: unittest.TestCase) -> None: + pass + + def _addexcinfo(self, rawexcinfo: _SysExcInfoType) -> None: + rawexcinfo = _handle_twisted_exc_info(rawexcinfo) + try: + excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info( + rawexcinfo # type: ignore[arg-type] + ) + # Invoke the attributes to trigger storing the traceback + # trial causes some issue there. + _ = excinfo.value + _ = excinfo.traceback + except TypeError: + try: + try: + values = traceback.format_exception(*rawexcinfo) + values.insert( + 0, + "NOTE: Incompatible Exception Representation, " + "displaying natively:\n\n", + ) + fail("".join(values), pytrace=False) + except (fail.Exception, KeyboardInterrupt): + raise + except BaseException: + fail( + "ERROR: Unknown Incompatible Exception " + f"representation:\n{rawexcinfo!r}", + pytrace=False, + ) + except KeyboardInterrupt: + raise + except fail.Exception: + excinfo = _pytest._code.ExceptionInfo.from_current() + self.__dict__.setdefault("_excinfo", []).append(excinfo) + + def addError( + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType + ) -> None: + try: + if isinstance(rawexcinfo[1], exit.Exception): + exit(rawexcinfo[1].msg) + except TypeError: + pass + self._addexcinfo(rawexcinfo) + + def addFailure( + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType + ) -> None: + self._addexcinfo(rawexcinfo) + + def addSkip(self, testcase: unittest.TestCase, reason: str) -> None: + try: + raise pytest.skip.Exception(reason, _use_item_location=True) + except skip.Exception: + self._addexcinfo(sys.exc_info()) + + def addExpectedFailure( + self, + testcase: unittest.TestCase, + rawexcinfo: _SysExcInfoType, + reason: str = "", + ) -> None: + try: + xfail(str(reason)) + except xfail.Exception: + self._addexcinfo(sys.exc_info()) + + def addUnexpectedSuccess( + self, + testcase: unittest.TestCase, + reason: twisted.trial.unittest.Todo | None = None, + ) -> None: + msg = "Unexpected success" + if reason: + msg += f": {reason.reason}" + # Preserve unittest behaviour - fail the test. Explicitly not an XPASS. + try: + fail(msg, pytrace=False) + except fail.Exception: + self._addexcinfo(sys.exc_info()) + + def addSuccess(self, testcase: unittest.TestCase) -> None: + pass + + def stopTest(self, testcase: unittest.TestCase) -> None: + pass + + def addDuration(self, testcase: unittest.TestCase, elapsed: float) -> None: + pass + + def runtest(self) -> None: + from _pytest.debugging import maybe_wrap_pytest_function_for_tracing + + testcase = self.instance + assert testcase is not None + + maybe_wrap_pytest_function_for_tracing(self) + + # Let the unittest framework handle async functions. + if is_async_function(self.obj): + testcase(result=self) + else: + # When --pdb is given, we want to postpone calling tearDown() otherwise + # when entering the pdb prompt, tearDown() would have probably cleaned up + # instance variables, which makes it difficult to debug. + # Arguably we could always postpone tearDown(), but this changes the moment where the + # TestCase instance interacts with the results object, so better to only do it + # when absolutely needed. + # We need to consider if the test itself is skipped, or the whole class. + assert isinstance(self.parent, UnitTestCase) + skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj) + if self.config.getoption("usepdb") and not skipped: + self._explicit_tearDown = testcase.tearDown + setattr(testcase, "tearDown", lambda *args: None) + + # We need to update the actual bound method with self.obj, because + # wrap_pytest_function_for_tracing replaces self.obj by a wrapper. + setattr(testcase, self.name, self.obj) + try: + testcase(result=self) + finally: + delattr(testcase, self.name) + + def _traceback_filter( + self, excinfo: _pytest._code.ExceptionInfo[BaseException] + ) -> _pytest._code.Traceback: + traceback = super()._traceback_filter(excinfo) + ntraceback = traceback.filter( + lambda x: not x.frame.f_globals.get("__unittest"), + ) + if not ntraceback: + ntraceback = traceback + return ntraceback + + +@hookimpl(tryfirst=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: + if isinstance(item, TestCaseFunction): + if item._excinfo: + call.excinfo = item._excinfo.pop(0) + try: + del call.result + except AttributeError: + pass + + # Convert unittest.SkipTest to pytest.skip. + # This is actually only needed for nose, which reuses unittest.SkipTest for + # its own nose.SkipTest. For unittest TestCases, SkipTest is already + # handled internally, and doesn't reach here. + unittest = sys.modules.get("unittest") + if unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest): + excinfo = call.excinfo + call2 = CallInfo[None].from_call( + lambda: pytest.skip(str(excinfo.value)), call.when + ) + call.excinfo = call2.excinfo + + +def _is_skipped(obj) -> bool: + """Return True if the given object has been marked with @unittest.skip.""" + return bool(getattr(obj, "__unittest_skip__", False)) + + +def pytest_configure() -> None: + """Register the TestCaseFunction class as an IReporter if twisted.trial is available.""" + if _get_twisted_version() is not TwistedVersion.NotInstalled: + from twisted.trial.itrial import IReporter + from zope.interface import classImplements + + classImplements(TestCaseFunction, IReporter) + + +class TwistedVersion(Enum): + """ + The Twisted version installed in the environment. + + We have different workarounds in place for different versions of Twisted. + """ + + # Twisted version 24 or prior. + Version24 = auto() + # Twisted version 25 or later. + Version25 = auto() + # Twisted version is not available. + NotInstalled = auto() + + +def _get_twisted_version() -> TwistedVersion: + # We need to check if "twisted.trial.unittest" is specifically present in sys.modules. + # This is because we intend to integrate with Trial only when it's actively running + # the test suite, but not needed when only other Twisted components are in use. + if "twisted.trial.unittest" not in sys.modules: + return TwistedVersion.NotInstalled + + import importlib.metadata + + import packaging.version + + version_str = importlib.metadata.version("twisted") + version = packaging.version.parse(version_str) + if version.major <= 24: + return TwistedVersion.Version24 + else: + return TwistedVersion.Version25 + + +# Name of the attribute in `twisted.python.Failure` instances that stores +# the `sys.exc_info()` tuple. +# See twisted.trial support in `pytest_runtest_protocol`. +TWISTED_RAW_EXCINFO_ATTR = "_twisted_raw_excinfo" + + +@hookimpl(wrapper=True) +def pytest_runtest_protocol(item: Item) -> Iterator[None]: + if _get_twisted_version() is TwistedVersion.Version24: + import twisted.python.failure as ut + + # Monkeypatch `Failure.__init__` to store the raw exception info. + original__init__ = ut.Failure.__init__ + + def store_raw_exception_info( + self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None + ): # pragma: no cover + if exc_value is None: + raw_exc_info = sys.exc_info() + else: + if exc_type is None: + exc_type = type(exc_value) + if exc_tb is None: + exc_tb = sys.exc_info()[2] + raw_exc_info = (exc_type, exc_value, exc_tb) + setattr(self, TWISTED_RAW_EXCINFO_ATTR, tuple(raw_exc_info)) + try: + original__init__( + self, exc_value, exc_type, exc_tb, captureVars=captureVars + ) + except TypeError: # pragma: no cover + original__init__(self, exc_value, exc_type, exc_tb) + + with MonkeyPatch.context() as patcher: + patcher.setattr(ut.Failure, "__init__", store_raw_exception_info) + return (yield) + else: + return (yield) + + +def _handle_twisted_exc_info( + rawexcinfo: _SysExcInfoType | BaseException, +) -> _SysExcInfoType: + """ + Twisted passes a custom Failure instance to `addError()` instead of using `sys.exc_info()`. + Therefore, if `rawexcinfo` is a `Failure` instance, convert it into the equivalent `sys.exc_info()` tuple + as expected by pytest. + """ + twisted_version = _get_twisted_version() + if twisted_version is TwistedVersion.NotInstalled: + # Unfortunately, because we cannot import `twisted.python.failure` at the top of the file + # and use it in the signature, we need to use `type:ignore` here because we cannot narrow + # the type properly in the `if` statement above. + return rawexcinfo # type:ignore[return-value] + elif twisted_version is TwistedVersion.Version24: + # Twisted calls addError() passing its own classes (like `twisted.python.Failure`), which violates + # the `addError()` signature, so we extract the original `sys.exc_info()` tuple which is stored + # in the object. + if hasattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR): + saved_exc_info = getattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR) + # Delete the attribute from the original object to avoid leaks. + delattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR) + return saved_exc_info # type:ignore[no-any-return] + return rawexcinfo # type:ignore[return-value] + elif twisted_version is TwistedVersion.Version25: + if isinstance(rawexcinfo, BaseException): + import twisted.python.failure + + if isinstance(rawexcinfo, twisted.python.failure.Failure): + tb = rawexcinfo.__traceback__ + if tb is None: + tb = sys.exc_info()[2] + return type(rawexcinfo.value), rawexcinfo.value, tb + + return rawexcinfo # type:ignore[return-value] + else: + # Ideally we would use assert_never() here, but it is not available in all Python versions + # we support, plus we do not require `type_extensions` currently. + assert False, f"Unexpected Twisted version: {twisted_version}" diff --git a/.venv/lib/python3.11/site-packages/_pytest/unraisableexception.py b/.venv/lib/python3.11/site-packages/_pytest/unraisableexception.py new file mode 100644 index 00000000..0faca36a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/unraisableexception.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import gc +import sys +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +# This is a stash item and not a simple constant to allow pytester to override it. +gc_collect_iterations_key = StashKey[int]() + + +def gc_collect_harder(iterations: int) -> None: + for _ in range(iterations): + gc.collect() + + +class UnraisableMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +unraisable_exceptions: StashKey[collections.deque[UnraisableMeta | BaseException]] = ( + StashKey() +) + + +def collect_unraisable(config: Config) -> None: + pop_unraisable = config.stash[unraisable_exceptions].pop + errors: list[pytest.PytestUnraisableExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_unraisable() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process unraisable exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) + except pytest.PytestUnraisableExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple unraisable exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[sys.UnraisableHookArgs], object] +) -> None: + # A single collection doesn't necessarily collect everything. + # Constant determined experimentally by the Trio project. + gc_collect_iterations = config.stash.get(gc_collect_iterations_key, 5) + try: + try: + gc_collect_harder(gc_collect_iterations) + collect_unraisable(config) + finally: + sys.unraisablehook = prev_hook + finally: + del config.stash[unraisable_exceptions] + + +def unraisable_hook( + unraisable: sys.UnraisableHookArgs, + /, + *, + append: Callable[[UnraisableMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the unraisablehook finishes and before the metadata object is + # collected by a pytest hook + err_msg = ( + "Exception ignored in" if unraisable.err_msg is None else unraisable.err_msg + ) + summary = f"{err_msg}: {unraisable.object!r}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + unraisable.exc_type, + unraisable.exc_value, + unraisable.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(unraisable.object) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + UnraisableMeta( + msg=msg, + cause_msg=cause_msg, + exc_value=unraisable.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_unraisable and once by the unraisablehook calling machinery + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = sys.unraisablehook + deque: collections.deque[UnraisableMeta | BaseException] = collections.deque() + config.stash[unraisable_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + sys.unraisablehook = functools.partial(unraisable_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_unraisable(item.config) diff --git a/.venv/lib/python3.11/site-packages/_pytest/warning_types.py b/.venv/lib/python3.11/site-packages/_pytest/warning_types.py new file mode 100644 index 00000000..5e78debb --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/warning_types.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import dataclasses +import inspect +from types import FunctionType +from typing import Any +from typing import final +from typing import Generic +from typing import TypeVar +import warnings + + +class PytestWarning(UserWarning): + """Base class for all warnings emitted by pytest.""" + + __module__ = "pytest" + + +@final +class PytestAssertRewriteWarning(PytestWarning): + """Warning emitted by the pytest assert rewrite module.""" + + __module__ = "pytest" + + +@final +class PytestCacheWarning(PytestWarning): + """Warning emitted by the cache plugin in various situations.""" + + __module__ = "pytest" + + +@final +class PytestConfigWarning(PytestWarning): + """Warning emitted for configuration issues.""" + + __module__ = "pytest" + + +@final +class PytestCollectionWarning(PytestWarning): + """Warning emitted when pytest is not able to collect a file or symbol in a module.""" + + __module__ = "pytest" + + +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """Warning class for features that will be removed in a future version.""" + + __module__ = "pytest" + + +class PytestRemovedIn9Warning(PytestDeprecationWarning): + """Warning class for features that will be removed in pytest 9.""" + + __module__ = "pytest" + + +@final +class PytestExperimentalApiWarning(PytestWarning, FutureWarning): + """Warning category used to denote experiments in pytest. + + Use sparingly as the API might change or even be removed completely in a + future version. + """ + + __module__ = "pytest" + + @classmethod + def simple(cls, apiname: str) -> PytestExperimentalApiWarning: + return cls(f"{apiname} is an experimental api that may change over time") + + +@final +class PytestReturnNotNoneWarning(PytestWarning): + """ + Warning emitted when a test function returns a value other than ``None``. + + See :ref:`return-not-none` for details. + """ + + __module__ = "pytest" + + +@final +class PytestUnknownMarkWarning(PytestWarning): + """Warning emitted on use of unknown markers. + + See :ref:`mark` for details. + """ + + __module__ = "pytest" + + +@final +class PytestUnraisableExceptionWarning(PytestWarning): + """An unraisable exception was reported. + + Unraisable exceptions are exceptions raised in :meth:`__del__ ` + implementations and similar situations when the exception cannot be raised + as normal. + """ + + __module__ = "pytest" + + +@final +class PytestUnhandledThreadExceptionWarning(PytestWarning): + """An unhandled exception occurred in a :class:`~threading.Thread`. + + Such exceptions don't propagate normally. + """ + + __module__ = "pytest" + + +_W = TypeVar("_W", bound=PytestWarning) + + +@final +@dataclasses.dataclass +class UnformattedWarning(Generic[_W]): + """A warning meant to be formatted during runtime. + + This is used to hold warnings that need to format their message at runtime, + as opposed to a direct message. + """ + + category: type[_W] + template: str + + def format(self, **kwargs: Any) -> _W: + """Return an instance of the warning category, formatted with given kwargs.""" + return self.category(self.template.format(**kwargs)) + + +@final +class PytestFDWarning(PytestWarning): + """When the lsof plugin finds leaked fds.""" + + __module__ = "pytest" + + +def warn_explicit_for(method: FunctionType, message: PytestWarning) -> None: + """ + Issue the warning :param:`message` for the definition of the given :param:`method` + + this helps to log warnings for functions defined prior to finding an issue with them + (like hook wrappers being marked in a legacy mechanism) + """ + lineno = method.__code__.co_firstlineno + filename = inspect.getfile(method) + module = method.__module__ + mod_globals = method.__globals__ + try: + warnings.warn_explicit( + message, + type(message), + filename=filename, + module=module, + registry=mod_globals.setdefault("__warningregistry__", {}), + lineno=lineno, + ) + except Warning as w: + # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message. + raise type(w)(f"{w}\n at {filename}:{lineno}") from None diff --git a/.venv/lib/python3.11/site-packages/_pytest/warnings.py b/.venv/lib/python3.11/site-packages/_pytest/warnings.py new file mode 100644 index 00000000..806681a5 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_pytest/warnings.py @@ -0,0 +1,152 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Generator +from contextlib import contextmanager +from contextlib import ExitStack +import sys +from typing import Literal +import warnings + +from _pytest.config import apply_warning_filters +from _pytest.config import Config +from _pytest.config import parse_warning_filter +from _pytest.main import Session +from _pytest.nodes import Item +from _pytest.terminal import TerminalReporter +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +@contextmanager +def catch_warnings_for_item( + config: Config, + ihook, + when: Literal["config", "collect", "runtest"], + item: Item | None, + *, + record: bool = True, +) -> Generator[None]: + """Context manager that catches warnings generated in the contained execution block. + + ``item`` can be None if we are not in the context of an item execution. + + Each warning captured triggers the ``pytest_warning_recorded`` hook. + """ + config_filters = config.getini("filterwarnings") + cmdline_filters = config.known_args_namespace.pythonwarnings or [] + with warnings.catch_warnings(record=record) as log: + if not sys.warnoptions: + # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). + warnings.filterwarnings("always", category=DeprecationWarning) + warnings.filterwarnings("always", category=PendingDeprecationWarning) + + # To be enabled in pytest 9.0.0. + # warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning) + + apply_warning_filters(config_filters, cmdline_filters) + + # apply filters from "filterwarnings" marks + nodeid = "" if item is None else item.nodeid + if item is not None: + for mark in item.iter_markers(name="filterwarnings"): + for arg in mark.args: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + try: + yield + finally: + if record: + # mypy can't infer that record=True means log is not None; help it. + assert log is not None + + for warning_message in log: + ihook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=warning_message, + nodeid=nodeid, + when=when, + location=None, + ) + ) + + +def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: + """Convert a warnings.WarningMessage to a string.""" + return warnings.formatwarning( + str(warning_message.message), + warning_message.category, + warning_message.filename, + warning_message.lineno, + warning_message.line, + ) + tracemalloc_message(warning_message.source) + + +@pytest.hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + with catch_warnings_for_item( + config=item.config, ihook=item.ihook, when="runtest", item=item + ): + return (yield) + + +@pytest.hookimpl(wrapper=True, tryfirst=True) +def pytest_collection(session: Session) -> Generator[None, object, object]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="collect", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_terminal_summary( + terminalreporter: TerminalReporter, +) -> Generator[None]: + config = terminalreporter.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_sessionfinish(session: Session) -> Generator[None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_load_initial_conftests( + early_config: Config, +) -> Generator[None]: + with catch_warnings_for_item( + config=early_config, ihook=early_config.hook, when="config", item=None + ): + return (yield) + + +def pytest_configure(config: Config) -> None: + with ExitStack() as stack: + stack.enter_context( + catch_warnings_for_item( + config=config, + ihook=config.hook, + when="config", + item=None, + # this disables recording because the terminalreporter has + # finished by the time it comes to reporting logged warnings + # from the end of config cleanup. So for now, this is only + # useful for setting a warning filter with an 'error' action. + record=False, + ) + ) + config.addinivalue_line( + "markers", + "filterwarnings(warning): add a warning filter to the given test. " + "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", + ) + config.add_cleanup(stack.pop_all().close) diff --git a/.venv/lib/python3.11/site-packages/_yaml/__init__.py b/.venv/lib/python3.11/site-packages/_yaml/__init__.py new file mode 100644 index 00000000..7baa8c4b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/METADATA b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/METADATA new file mode 100644 index 00000000..28026c3b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/METADATA @@ -0,0 +1,212 @@ +Metadata-Version: 2.4 +Name: aboutcode-toolkit +Version: 11.1.1 +Summary: AboutCode-toolkit is a tool to document the provenance (origin and license) of third-party software using small text files. Collect inventories and generate attribution documentation. +Home-page: https://github.com/nexB/aboutcode-toolkit +Author: nexB. Inc. and others +Author-email: info@aboutcode.org +License: Apache-2.0 +Keywords: license,about,metadata,package,copyright,attribution,software,inventory,open source,sca,SBOM,spdx +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Documentation +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: apache-2.0.LICENSE +License-File: NOTICE +License-File: AUTHORS.rst +License-File: CHANGELOG.rst +License-File: CODE_OF_CONDUCT.rst +Requires-Dist: attrs +Requires-Dist: boolean.py>=3.5 +Requires-Dist: certifi +Requires-Dist: click +Requires-Dist: jinja2 +Requires-Dist: license_expression>=0.94 +Requires-Dist: openpyxl +Requires-Dist: packageurl_python>=0.9.0 +Requires-Dist: requests +Requires-Dist: saneyaml +Provides-Extra: testing +Requires-Dist: pytest!=7.0.0,>=6; extra == "testing" +Requires-Dist: pytest-xdist>=2; extra == "testing" +Requires-Dist: twine; extra == "testing" +Requires-Dist: black; extra == "testing" +Requires-Dist: isort; extra == "testing" +Provides-Extra: docs +Requires-Dist: Sphinx>=5.0.2; extra == "docs" +Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "docs" +Requires-Dist: sphinx-reredirects>=0.1.2; extra == "docs" +Requires-Dist: doc8>=0.11.2; extra == "docs" +Dynamic: license-file + +================= +AboutCode Toolkit +================= + +Introduction +------------ +The AboutCode Toolkit and ABOUT files provide a simple way to document the +origin, license, usage and other important or interesting information about +third-party software components that you use in your project. + +You start by storing ABOUT files (a small YAML formatted text file with field/value pairs) +side-by-side with each of the third-party software components you use. +Each ABOUT file documents origin and license for one software. +There are many examples of ABOUT files (valid or invalid) in the testdata/ +directory of the whole repository. + +The current version of the AboutCode Toolkit can read these ABOUT files so that you +can collect and validate the inventory of third-party components that you use. + +In addition, this tool is able to generate attribution notices and +identify redistributable source code used in your project to help you comply +with open source licenses conditions. + +This version of the AboutCode Toolkit follows the ABOUT specification version 3.3.2 at: +https://aboutcode-toolkit.readthedocs.io/en/latest/specification.html + + +Build and tests status +---------------------- + ++-------+-----------------+--------------+ +|Branch | **Linux/macOS** | **Windows** | ++=======+=================+==============+ +|Master | |master-posix| | |master-win| | ++-------+-----------------+--------------+ +|Develop| |devel-posix| | |devel-win| | ++-------+-----------------+--------------+ + + +REQUIREMENTS +------------ +The AboutCode Toolkit is tested with Python 3.9 or above only on Linux, Mac and Windows. +You will need to install a Python interpreter if you do not have one already +installed. + +On Linux and Mac, Python is typically pre-installed. To verify which +version may be pre-installed, open a terminal and type: + + python --version + +Note +~~~~ + Debian has decided that distutils is not a core python package, so it is not included in the last versions of debian and debian-based OSes. + A solution is to run: `sudo apt install python3-distutils` + +On Windows or Mac, you can download the latest Python here: + https://www.python.org/downloads/ + +Download the .msi installer for Windows or the .dmg archive for Mac. +Open and run the installer using all the default options. + +INSTALLATION +------------ +Checkout or download and extract the AboutCode Toolkit from: + https://github.com/nexB/aboutcode-toolkit/ + +To install all the needed dependencies in a virtualenv, run (on posix): + ./configure +or on windows: + configure + +ACTIVATE the VIRTUALENV +----------------------- +To activate the virtualenv, run (on posix): + source venv/bin/activate +or on windows: + venv\\bin\\activate + + +DEACTIVATE the VIRTUALENV +------------------------- +To deactivate the virtualenv, run (on both posix and windows): + deactivate + + +VERSIONING SCHEMA +----------------- +Starting at AboutCode version 4.0.0, the AboutCode Toolkit will follow SemVer for the versioning schema. + +i.e. MAJOR.MINOR.PATCH format + 1. MAJOR version when making incompatible API changes, + 2. MINOR version when making functionality in a backwards compatible manner, and + 3. PATCH version when making backwards compatible bug fixes. + + +REFERENCE +--------- +See https://aboutcode-toolkit.readthedocs.io/en/latest/ for documentation. + +See https://aboutcode-toolkit.readthedocs.io/en/latest/reference.html for reference. + +TESTS and DEVELOPMENT +--------------------- +To install all the needed development dependencies, run (on posix): + ./configure --dev +or on windows: + configure --dev + +To verify that everything works fine you can run the test suite with: + pytest + + +CLEAN BUILD AND INSTALLED FILES +------------------------------- +To clean the built and installed files, run (on posix): + ./configure --clean +or on windows: + configure --clean + + +HELP and SUPPORT +---------------- +If you have a question or find a bug, enter a ticket at: + + https://github.com/nexB/aboutcode-toolkit + +For issues, you can use: + + https://github.com/nexB/aboutcode-toolkit/issues + + +SOURCE CODE +----------- +The AboutCode Toolkit is available through GitHub. For the latest version visit: + https://github.com/nexB/aboutcode-toolkit + + +HACKING +------- +We accept pull requests provided under the same license as this tool. +You agree to the http://developercertificate.org/ + + +LICENSE +------- +The AboutCode Toolkit is released under the Apache 2.0 license. +See (of course) the about.ABOUT file for details. + + +.. |master-posix| image:: https://api.travis-ci.org/nexB/aboutcode-toolkit.png?branch=master + :target: https://travis-ci.org/nexB/aboutcode-toolkit + :alt: Linux Master branch tests status +.. |devel-posix| image:: https://api.travis-ci.org/nexB/aboutcode-toolkit.png?branch=develop + :target: https://travis-ci.org/nexB/aboutcode-toolkit + :alt: Linux Develop branch tests status + +.. |master-win| image:: https://ci.appveyor.com/api/projects/status/uwj2gh8i9ga1mqwn/branch/master?png=true + :target: https://ci.appveyor.com/project/nexB/aboutcode-toolkit + :alt: Windows Master branch tests status +.. |devel-win| image:: https://ci.appveyor.com/api/projects/status/uwj2gh8i9ga1mqwn/branch/develop?png=true + :target: https://ci.appveyor.com/project/nexB/aboutcode-toolkit + :alt: Windows Develop branch tests status diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/RECORD b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/RECORD new file mode 100644 index 00000000..6d4d9157 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/RECORD @@ -0,0 +1,40 @@ +../../../bin/about,sha256=m4lnwdlDD6lbw7IYja0bsYnUShDujZmRqNQmX6LWBD0,281 +aboutcode_toolkit-11.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aboutcode_toolkit-11.1.1.dist-info/METADATA,sha256=8w3kUpqqmZRN4obTPyOaOn3JNzxflIb9UjewW3Jv5FU,7336 +aboutcode_toolkit-11.1.1.dist-info/RECORD,, +aboutcode_toolkit-11.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +aboutcode_toolkit-11.1.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91 +aboutcode_toolkit-11.1.1.dist-info/entry_points.txt,sha256=CZT0HhqtyGSDg-3k31FJ-dBJCZWVriiAriiWezqkC54,50 +aboutcode_toolkit-11.1.1.dist-info/licenses/AUTHORS.rst,sha256=ZjbduGIWwLqKhp-kADJzkum26DpGrYiMS8TkJMtxTN8,88 +aboutcode_toolkit-11.1.1.dist-info/licenses/CHANGELOG.rst,sha256=grSi1Cby0Fl23z1zyfylOSQW1V2NqpBeGoQ9TfCl73w,12767 +aboutcode_toolkit-11.1.1.dist-info/licenses/CODE_OF_CONDUCT.rst,sha256=xXDTFpYY3Y7JoxrWADihieUvhGlwJc_oxsbmLJaBvE4,3422 +aboutcode_toolkit-11.1.1.dist-info/licenses/NOTICE,sha256=SM7ELF-ckyHeexaDSX7P2EqDbjYkb6S8c_CWcsdMyXM,752 +aboutcode_toolkit-11.1.1.dist-info/licenses/apache-2.0.LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558 +aboutcode_toolkit-11.1.1.dist-info/top_level.txt,sha256=AZqc0bTnzp-gGSKh0dHQ6HyO7yAidVQFShB88ntF_yA,14 +attributecode/__init__.py,sha256=Gp1kQdT1M5P9Sqtsrqu5k4ICYfaH81u0jXC90hHPp9E,3524 +attributecode/__main__.py,sha256=vQg4laOfHZB8a66tnRd2sjgwnTDPPN7dF3oYJJErFQg,921 +attributecode/__pycache__/__init__.cpython-311.pyc,, +attributecode/__pycache__/__main__.cpython-311.pyc,, +attributecode/__pycache__/api.cpython-311.pyc,, +attributecode/__pycache__/attrib.cpython-311.pyc,, +attributecode/__pycache__/attrib_util.cpython-311.pyc,, +attributecode/__pycache__/cmd.cpython-311.pyc,, +attributecode/__pycache__/gen.cpython-311.pyc,, +attributecode/__pycache__/licenses.cpython-311.pyc,, +attributecode/__pycache__/model.cpython-311.pyc,, +attributecode/__pycache__/transform.cpython-311.pyc,, +attributecode/__pycache__/util.cpython-311.pyc,, +attributecode/api.py,sha256=kjMEd4KyVQBT5tKWtShry2px0uoD8klS0OqUZEpuTZI,3343 +attributecode/attrib.py,sha256=LO9jPbAJcuo3L9FLlTUnt3h1HdjfeA_EiwUDqkC7X-E,13758 +attributecode/attrib_util.py,sha256=Jf3CeUQtO--RIlRWIPgICp6rUpILChgyY_dO0yugHkE,4274 +attributecode/cmd.py,sha256=G_NGNS97Lf6GxhU5FRG9ai0ZxYdtth8XeGfav_ak1R0,36036 +attributecode/gen.py,sha256=nI-0MNWBePEZwUFpUBDYEt4Tym3K1q6F_ZwSnVCZJ0g,14291 +attributecode/licenses.py,sha256=jSDAc9VlMhMwS_ciwdkvBFNFUMRPO2uxojT6SVctCKA,2050 +attributecode/model.py,sha256=6AqpGZvhSIRnk68l8x_Rz-wwYPwsB-mJlpUdRyAdGhQ,86303 +attributecode/templates/default_html.template,sha256=Gguh-iEGZ7WPWgh6TXjEDe20ixz0SVljtqMlSHjexRE,3368 +attributecode/templates/default_json.template,sha256=XWkJ49WUZhOTlU11ETDG6zsjClvwN_a6vzwnZg6KPbQ,768 +attributecode/templates/license_ref.template,sha256=Kmrdw1aCAGQNKhuLqubiEKOL6DBZO8zJm2xIgZaZRc4,2631 +attributecode/templates/list.csv,sha256=60O3BXox2NwRQPI8k9-LxfnactTLuw1B1tuqx0WkWOw,165 +attributecode/templates/scancode_html.template,sha256=_YhH0dOh2zDNlubXXyCi_CgglCpfz_CrfzFB4VXNZ1U,3296 +attributecode/transform.py,sha256=1hEcJiip6rrPGVj4viYxR6pjGsnFdAsCCjSZtphwlhY,14865 +attributecode/util.py,sha256=VeCfVamZIqnBtUPhv1tErxihEmtOlz5Ki5r6i8tZiCg,28425 diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/WHEEL new file mode 100644 index 00000000..1eb3c49d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/entry_points.txt b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/entry_points.txt new file mode 100644 index 00000000..052318bc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +about = attributecode.cmd:about diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/AUTHORS.rst b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/AUTHORS.rst new file mode 100644 index 00000000..2e919829 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/AUTHORS.rst @@ -0,0 +1,3 @@ +The following organizations or individuals have contributed to this repo: + +- nexB, Inc. diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CHANGELOG.rst b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CHANGELOG.rst new file mode 100644 index 00000000..3bd5cb09 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CHANGELOG.rst @@ -0,0 +1,433 @@ +============================== +Changelog + +2025-03-31 + Release 11.1.1 + + * Deleted unused files + * Updated AppVeyor configuration to use Python 3.9 + * Modified the PyPI release script to utilize upload-artifact@v4 + + +2025-03-28 + Release 11.1.0 + + * Drop support for python version earlier than 3.9 + * Add ability to "exclude" path in the check and inventory #583 + * Add support for the special '-' FILE to print to on screen/to stdout in inventory #584 + + +2024-09-16 + Release 11.0.2 + + * Fixed the installation issues with docker (#571, #572) + + +2024-08-06 + Release 11.0.1 + + * Update doc formatting + * Added fields type and types description into the spec + * Update link references of ownership from nexB to aboutcode-org + + +2024-07-15 + Release 11.0.0 + + * Add character support (at most 2 characters) for `attribute` field + * Strip empty newline characters when loading an inventory + * Catch invalid license_expression + * Update the specification to 3.3.2 + * Support "declared_license_expression" and "other_license_expression" + * Updated "about_resource" to be an optional field + * Updated spec to v4.0.0 as moving `about_resource` from mandatory to optional + + +2023-09-25 + Release 10.1.0 + + * Fixed `transform` with nested list #531 + * Added curl dependency in Dockerfile #532 + * Introduce spdx_license_expression + * Ability to transform spdx license key from spdx_license_expression to + license_expression (i.e. Generate attribution with + spdx_license_expression) #513 + * Ability to configure the proxy settings #533 + * Fixed licenses issue #534 + +2023-08-20 + Release 10.0.0 + + * Fixd error in load_json in util.py + * Code cleanup + * Work with the SCTK version 32 and later (Drop support for SCTK version 31 and earlier) + * Implement `--scancode` option for `gen` + + +2023-07-14 + Release 9.0.0 + + * The tool will now show which worksheet (if .xlsx input) is the tool working on + * Error handling if defined worksheet does not exist + * Adopt 3.3.1 specification: introduce ``ignored_resources`` + + +2023-03-09 + Release 8.0.0 + + * Fixed the transform code for xlsx and json + * Remove irrelevant error for attrib + * Add support to identify worksheet name for XLSX input + * The severity error level for "contains illegal name characters (or empty spaces) and is ignored" is changed from ERROR to WARNING + * Remove the limitation to ASCII only + * Drop support for python3.6 + * Update valid chatacters for file/path name + * Adopt 3.3.0 specification + + +2022-10-24 + Release 7.2.0 + + * Add option to validate `license_expression` in the `check` command + + +2022-10-24 + Release 7.1.1 + + * This new release has no feature changes. + + +2022-10-24 + Release 7.1.0 + + * Fixed version mismatch (https://github.com/nexB/aboutcode-toolkit/issues/510) + * Improve `check` performance (https://github.com/nexB/aboutcode-toolkit/issues/511) + * Relax the requirement to have the same format for input and output for `transform` + * Collect and handle the "matched_text" from the `--license-text` option from the scancode-toolkit + + +2022-03-21 + Release 7.0.2 + + * Relax dependency constraints + * Use latest skeleton and settings + + +2022-03-21 + Release 7.0.1 + + * Bump openpyxl to 3.0.9 + * Better handling of invalid API URL + * Better formatting for default HTML template + +2022-03-01 + Release 7.0.0 + + * Add '@' as a supported character for filename #451 + * Add support to collect redistributable sources #22 + * Handle trailing spaces in field names during `transform` #456 + * Remove restriction of python27 only on windows #453 + * Documentation updated + * Code enhancement + * Remove thirdparty/ + * Update configuration scripts + * Use readthedocs for documentation + * Add Dockerfile to run aboutcode with docker + * Add new option to choose extract license from ScanCode LicenseDB or DJC License Library + * Add ability to transform XLSX file + * Support XLSX file format for `inventory`, `gen` and `attrib` + * Add 'spdx_license_key' support + * Add option to save error log in `check` command + * New `gen_license` option + * Bump PyYAML to 6.0 + * Add '%" as a supported character + * Update default template + * All errors are logged if and only if the `verbose` option is set. Otherwise, ony 'Critical' and 'Warning' errors will be showed/logged + * Ability to generate attribution notice directly from an input inventory + * Remove the restriction of requiring 'about_resource' field in the input if performing `attrib` from an inventory + +2021-04-02 + Release 6.0.0 + + * This new release has no feature changes. + * It has relaxed PyYAML dependencies and drop Python 2 support + +2020-09-01 + Release 5.1.0 + + * Add support for `package_url` #396 + * Fixed #443 and #444 issue with multiple licenses/license_files + * Fixed #442 no special characters allowed for `license_key`, `license_name` and `license_expression` + * Fixed #446 Better error handling + +2020-08-11 + Release 5.0.0 + + * Enhance the `transform` to also work with JSON file + * Update transform code (See #427 and #428) + * Fixed #431 - Error handling for empty "_file" fields + * Fixed #432 - Handled UTF-8 variant invented by Microsoft + * Fixed #433 - problem was caused by the different multi-lic file format between json and CSV (CSV with '\n' line break) + * Fixed #436 - issue about copy with the `--reference` option + * Fixed #396 - support for alternative output for Android + +2020-05-05 + Release 4.0.2 + + * Upgrade license-expression library to v1.2 + * Fix the missing `multi_sort` filter for Jinja2 + * Update help text for `--vartext` + +2019-10-17 + Release 4.0.1 + + * Declare to follow SemVer for versioning schema + * Update REFERENCE.rst and README.rst + * Update license-expression library + + +2019-10-09 + + Release 4.0.0 + + * Support filenames/path with special characters #310 #378 #392 + * Update ABOUT file format to match the specification + * Log version of which AbcTK was used #397 + * Fix the licenses (key, name, file) not in sync issue #406 + * Correct invalid msg for boolean fields #403 + * Remove the `about_file_path` key/column from input/output #364 + * Use ',' to support multiple files #404 + * Fix bugs in `transform` #408, #412 + +2018-11-15 + + Release 3.3.0 + + * Update the list of common license keys + * New UrlListField introduced for list of urls + * The UrlField is now only taking single URL value + * The owner is now a StringField instead of ListField + * Format the ordering of the generated ABOUT file (See https://github.com/nexB/aboutcode-toolkit/issues/349#issuecomment-438871444) + * '+' and '(' and ')' is now supported in license_expression + * The key 'about_resource_path' is removed + * Revert back the requirement of the 'name' field + * Add a new supported 'internal_use_only' key + +2018-10-23 + + Release 3.2.2 + + * Fix the version number + * `name` field is no longer a required field + +2018-10-22 + + Release 3.2.1 + + * The 'license' field is now become 'license_key' + * Multiple licenses support + * No support for referenceing multuiple resources + * Fix the incorrect boolean field behaviors + * Display number of errors/warnings in the error log + +2018-09-19 + + Release 3.1.3 + + * Minor update + +2018-09-19 + + Release 3.1.2 + + * New `--vartext` option for `attrib` + * Add support for `checksum_sha256` and `author_file` + * `check` command will not count INFO message as error when `--verbose` is set + * Update `track_change` to `track_changes` + * New `--filter` and `--mapping-output` options for `inventory` + +2018-6-25 + + Release 3.1.1 + + * No support of multiple occurrence keys in the input + * Updated the specification document + * Fixed bug that cause template processing error in attrib + + etc... + + +2018-6-8 Chin-Yeung Li + + Release 3.1.0 + + * Fixed JSON input from AboutCode manger export and ScanCode output + * Added a new option `mapping-file` to support using a custom file for mapping + * Change the name of the option `--show-all` to `--verbose` + * Better error handling for copying file with permission issue + * Support timestamp in attribution output + + etc... + + +2017-11-17 Chin-Yeung Li + + Release 3.0.* + + ABOUT files is now YAML formatted. + Supported license expression. + Supported JSON input and output format: https://github.com/nexB/aboutcode-toolkit/issues/246 and https://github.com/nexB/aboutcode-toolkit/issues/277 + Support Python 3: https://github.com/nexB/aboutcode-toolkit/issues/280 + Refined help texts + Refined USAGE texts + Refined SPECs + + Input key changes: + ================== + `about_file` is replaced by `about_file_path` + `dje_license_key` is replaced by `license_expression` + `version` is no longer a required field + `home_url` is now `homepage_url` + + API Updated: + ============ + - Break down the 3 major functions: `inventory`, `genabout` and `genattrib` into 3 subcommands: + i.e. + `about inventory`, `about generate` and `about attrib` + + - A new `check` subcommand: https://github.com/nexB/aboutcode-toolkit/issues/281 + + - Some options changes + `--extract_license` becomes `--fetch-license` + `--license_text_location` becomes `--license-notice-text-location` + + etc... + + +2016-06-27 Chin-Yeung Li + + Release 2.3.2 + + * Documentation updates + + +2016-03-29 Philippe Ombredanne + + Release 2.3.1 + + * Various minor bug fixes and improvements + * Support for the latest DejaCode API if you use DejaCode + + +2016-03-28 Philippe Ombredanne + + Release 2.3.0 + + * Various minor bug fixes and improvements + * Support for the latest DejaCode API if you use DejaCode + + +2015-10-23 Chin-Yeung Li + + Release 2.2.0 + + * Improved CLI error messages + * Fixed the filtering of dicts with empty values. + * Refined help texts + * Updated configure script + * Refactorings and code simplifications + * Fixed misleading error message when using invalid api_url + + +2015-10-09 Chin-Yeung Li + + Release 2.1.0 + + * Minor code refactoring + * Handle long path error on Windows OS when using genattrib with a zip + + +2015-09-29 Chin-Yeung Li + + Release 2.0.4 + + * Added support to run genattrib with a zip file and tests + * Display a "Completed" message once the generation is completed + + +2015-08-01 Chin-Yeung Li + + Release 2.0.3 + + * Fix the bug of using genattrib.py on Windows OS + * Display version when running about.py, genabout.py and genattrib.py + + +2015-07-07 Chin-Yeung Li + + Release 2.0.2 + + * Handle input's encoding issues + * Better error handling + * Writing to and reading from Windows OS with paths > 255 chars + + +2015-06-09 Chin-Yeung Li + + Release 2.0.1 + + * Configuration script fixes and updates basic documentation. + + +2015-05-05 Chin-Yeung Li + + Release 2.0.0 + + * Breaking API changes: + + * the dje_license field has been renamed to dje_license_key + * when a dje_license_key is present, a new dje_license_url will be + reported when fetching data from the DejaCode API. + * In genabout, the '--all_in_one' command line option has been removed. + It was not well specified and did not work as advertised. + + * in genattrib: + + * the Component List is now optional. + * there is a new experimental '--verification_location' command line + option. This option will be removed in the future version. Do not use + it. + * the '+' character is now supported in file names. + * several bugs have been fixed. + * error handling and error and warning reporting have been improved. + * New documentation in doc: UsingAboutCodetoDocumentYourSoftwareAssets.pdf + + +2014-11-05 Philippe Ombredanne + + Release 1.0.2 + + * Minor bug fixes and improved error reporting. + + +2014-11-03 Philippe Ombredanne + + Release 1.0.1 + + * Minor bug fixes, such as extraneous debug printouts. + + +2014-10-31 Philippe Ombredanne + + Release 1.0.0 + + * Some changes in the spec, such as supporting only text in external + files. + * Several refinements including support for common licenses. + +2014-06-24 Chin-Yeung Li + + Release 0.8.1 + + * Initial release with minimal capabilities to read and validate + ABOUT files format 0.8.0 and output a CSV inventory. diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CODE_OF_CONDUCT.rst b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CODE_OF_CONDUCT.rst new file mode 100644 index 00000000..590ba198 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/CODE_OF_CONDUCT.rst @@ -0,0 +1,86 @@ +Contributor Covenant Code of Conduct +==================================== + +Our Pledge +---------- + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our +project and our community a harassment-free experience for everyone, +regardless of age, body size, disability, ethnicity, gender identity and +expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +Our Standards +------------- + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual + attention or advances +- Trolling, insulting/derogatory comments, and personal or political + attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or + electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities +-------------------- + +Project maintainers are responsible for clarifying the standards of +acceptable behavior and are expected to take appropriate and fair +corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, +or reject comments, commits, code, wiki edits, issues, and other +contributions that are not aligned to this Code of Conduct, or to ban +temporarily or permanently any contributor for other behaviors that they +deem inappropriate, threatening, offensive, or harmful. + +Scope +----- + +This Code of Conduct applies both within project spaces and in public +spaces when an individual is representing the project or its community. +Examples of representing a project or community include using an +official project e-mail address, posting via an official social media +account, or acting as an appointed representative at an online or +offline event. Representation of a project may be further defined and +clarified by project maintainers. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior may +be reported by contacting the project team at pombredanne@gmail.com +or on the Gitter chat channel at https://gitter.im/aboutcode-org/discuss . +All complaints will be reviewed and investigated and will result in a +response that is deemed necessary and appropriate to the circumstances. +The project team is obligated to maintain confidentiality with regard to +the reporter of an incident. Further details of specific enforcement +policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in +good faith may face temporary or permanent repercussions as determined +by other members of the project’s leadership. + +Attribution +----------- + +This Code of Conduct is adapted from the `Contributor Covenant`_ , +version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +.. _Contributor Covenant: https://www.contributor-covenant.org diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/NOTICE b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/NOTICE new file mode 100644 index 00000000..65936b2b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/NOTICE @@ -0,0 +1,19 @@ +# +# Copyright (c) nexB Inc. and others. +# SPDX-License-Identifier: Apache-2.0 +# +# Visit https://aboutcode.org and https://github.com/nexB/ for support and download. +# ScanCode is a trademark of nexB Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/apache-2.0.LICENSE b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/apache-2.0.LICENSE new file mode 100644 index 00000000..29f81d81 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/licenses/apache-2.0.LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/top_level.txt new file mode 100644 index 00000000..6212f86e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/aboutcode_toolkit-11.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +attributecode diff --git a/.venv/lib/python3.11/site-packages/attr/__init__.py b/.venv/lib/python3.11/site-packages/attr/__init__.py new file mode 100644 index 00000000..5c6e0650 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/__init__.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: MIT + +""" +Classes Without Boilerplate +""" + +from functools import partial +from typing import Callable, Literal, Protocol + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Converter, + Factory, + _Nothing, + attrib, + attrs, + evolve, + fields, + fields_dict, + make_class, + validate, +) +from ._next_gen import define, field, frozen, mutable +from ._version_info import VersionInfo + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + + +class AttrsInstance(Protocol): + pass + + +NothingType = Literal[_Nothing.NOTHING] + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "Converter", + "Factory", + "NothingType", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "get_run_validators", + "has", + "ib", + "make_class", + "mutable", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + + +def _make_getattr(mod_name: str) -> Callable: + """ + Create a metadata proxy for packaging information that uses *mod_name* in + its warnings and errors. + """ + + def __getattr__(name: str) -> str: + if name not in ("__version__", "__version_info__"): + msg = f"module {mod_name} has no attribute {name}" + raise AttributeError(msg) + + from importlib.metadata import metadata + + meta = metadata("attrs") + + if name == "__version_info__": + return VersionInfo._from_version_string(meta["version"]) + + return meta["version"] + + return __getattr__ + + +__getattr__ = _make_getattr(__name__) diff --git a/.venv/lib/python3.11/site-packages/attr/__init__.pyi b/.venv/lib/python3.11/site-packages/attr/__init__.pyi new file mode 100644 index 00000000..8d78fa19 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/__init__.pyi @@ -0,0 +1,389 @@ +import enum +import sys + +from typing import ( + Any, + Callable, + Generic, + Literal, + Mapping, + Protocol, + Sequence, + TypeVar, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._cmp import cmp_using as cmp_using +from ._typing_compat import AttrsInstance_ +from ._version_info import VersionInfo +from attrs import ( + define as define, + field as field, + mutable as mutable, + frozen as frozen, + _EqOrderType, + _ValidatorType, + _ConverterType, + _ReprArgType, + _OnSetAttrType, + _OnSetAttrArgType, + _FieldTransformer, + _ValidatorArgType, +) + +if sys.version_info >= (3, 10): + from typing import TypeGuard, TypeAlias +else: + from typing_extensions import TypeGuard, TypeAlias + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_FilterType = Callable[["Attribute[_T]", _T], bool] + +# We subclass this here to keep the protocol's qualified name clean. +class AttrsInstance(AttrsInstance_, Protocol): + pass + +_A = TypeVar("_A", bound=type[AttrsInstance]) + +class _Nothing(enum.Enum): + NOTHING = enum.auto() + +NOTHING = _Nothing.NOTHING +NothingType: TypeAlias = Literal[_Nothing.NOTHING] + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. + +@overload +def Factory(factory: Callable[[], _T]) -> _T: ... +@overload +def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], +) -> _T: ... +@overload +def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], +) -> _T: ... + +In = TypeVar("In") +Out = TypeVar("Out") + +class Converter(Generic[In, Out]): + @overload + def __init__(self, converter: Callable[[In], Out]) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance, Attribute], Out], + *, + takes_self: Literal[True], + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, Attribute], Out], + *, + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance], Out], + *, + takes_self: Literal[True], + ) -> None: ... + +class Attribute(Generic[_T]): + name: str + default: _T | None + validator: _ValidatorType[_T] | None + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: bool | None + init: bool + converter: Converter | None + metadata: dict[Any, Any] + type: type[_T] | None + kw_only: bool + on_setattr: _OnSetAttrType + alias: str | None + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: object = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: _C, + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> _C: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> Callable[[_C], _C]: ... +def fields(cls: type[AttrsInstance]) -> Any: ... +def fields_dict(cls: type[AttrsInstance]) -> dict[str, Attribute[Any]]: ... +def validate(inst: AttrsInstance) -> None: ... +def resolve_types( + cls: _A, + globalns: dict[str, Any] | None = ..., + localns: dict[str, Any] | None = ..., + attribs: list[Attribute[Any]] | None = ..., + include_extras: bool = ..., +) -> _A: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: list[str] | tuple[str, ...] | dict[str, Any], + bases: tuple[type, ...] = ..., + class_body: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + collect_by_mro: bool = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +# XXX: remember to fix attrs.asdict/astuple too! +def asdict( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + dict_factory: type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ..., + tuple_keys: bool | None = ..., +) -> dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + tuple_factory: type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> tuple[Any, ...]: ... +def has(cls: type) -> TypeGuard[type[AttrsInstance]]: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/.venv/lib/python3.11/site-packages/attr/_cmp.py b/.venv/lib/python3.11/site-packages/attr/_cmp.py new file mode 100644 index 00000000..09bab491 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_cmp.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: MIT + + +import functools +import types + +from ._make import __ne__ + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, + and ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if at least + one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + Args: + eq (typing.Callable | None): + Callable used to evaluate equality of two objects. + + lt (typing.Callable | None): + Callable used to evaluate whether one object is less than another + object. + + le (typing.Callable | None): + Callable used to evaluate whether one object is less than or equal + to another object. + + gt (typing.Callable | None): + Callable used to evaluate whether one object is greater than + another object. + + ge (typing.Callable | None): + Callable used to evaluate whether one object is greater than or + equal to another object. + + require_same_type (bool): + When `True`, equality and ordering methods will return + `NotImplemented` if objects are not of the same type. + + class_name (str | None): Name of class. Defaults to "Comparable". + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = __ne__ + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = types.new_class( + class_name, (object,), {}, lambda ns: ns.update(body) + ) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + msg = "eq must be define is order to complete ordering from lt, le, gt, ge." + raise ValueError(msg) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = f"__{name}__" + method.__doc__ = ( + f"Return a {_operation_names[name]} b. Computed by attrs." + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + return all(func(self, other) for func in self._requirements) + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/.venv/lib/python3.11/site-packages/attr/_cmp.pyi b/.venv/lib/python3.11/site-packages/attr/_cmp.pyi new file mode 100644 index 00000000..cc7893b0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_cmp.pyi @@ -0,0 +1,13 @@ +from typing import Any, Callable + +_CompareWithType = Callable[[Any, Any], bool] + +def cmp_using( + eq: _CompareWithType | None = ..., + lt: _CompareWithType | None = ..., + le: _CompareWithType | None = ..., + gt: _CompareWithType | None = ..., + ge: _CompareWithType | None = ..., + require_same_type: bool = ..., + class_name: str = ..., +) -> type: ... diff --git a/.venv/lib/python3.11/site-packages/attr/_compat.py b/.venv/lib/python3.11/site-packages/attr/_compat.py new file mode 100644 index 00000000..bc68ed9e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_compat.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: MIT + +import inspect +import platform +import sys +import threading + +from collections.abc import Mapping, Sequence # noqa: F401 +from typing import _GenericAlias + + +PYPY = platform.python_implementation() == "PyPy" +PY_3_10_PLUS = sys.version_info[:2] >= (3, 10) +PY_3_11_PLUS = sys.version_info[:2] >= (3, 11) +PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) +PY_3_13_PLUS = sys.version_info[:2] >= (3, 13) +PY_3_14_PLUS = sys.version_info[:2] >= (3, 14) + + +if PY_3_14_PLUS: + import annotationlib + + # We request forward-ref annotations to not break in the presence of + # forward references. + + def _get_annotations(cls): + return annotationlib.get_annotations( + cls, format=annotationlib.Format.FORWARDREF + ) + +else: + + def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + return cls.__dict__.get("__annotations__", {}) + + +class _AnnotationExtractor: + """ + Extract type annotations from a callable, returning None whenever there + is none. + """ + + __slots__ = ["sig"] + + def __init__(self, callable): + try: + self.sig = inspect.signature(callable) + except (ValueError, TypeError): # inspect failed + self.sig = None + + def get_first_param_type(self): + """ + Return the type annotation of the first argument if it's not empty. + """ + if not self.sig: + return None + + params = list(self.sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + return params[0].annotation + + return None + + def get_return_type(self): + """ + Return the return type if it's not empty. + """ + if ( + self.sig + and self.sig.return_annotation is not inspect.Signature.empty + ): + return self.sig.return_annotation + + return None + + +# Thread-local global to track attrs instances which are already being repr'd. +# This is needed because there is no other (thread-safe) way to pass info +# about the instances that are already being repr'd through the call stack +# in order to ensure we don't perform infinite recursion. +# +# For instance, if an instance contains a dict which contains that instance, +# we need to know that we're already repr'ing the outside instance from within +# the dict's repr() call. +# +# This lives here rather than in _make.py so that the functions in _make.py +# don't have a direct reference to the thread-local in their globals dict. +# If they have such a reference, it breaks cloudpickle. +repr_context = threading.local() + + +def get_generic_base(cl): + """If this is a generic class (A[str]), return the generic base for it.""" + if cl.__class__ is _GenericAlias: + return cl.__origin__ + return None diff --git a/.venv/lib/python3.11/site-packages/attr/_config.py b/.venv/lib/python3.11/site-packages/attr/_config.py new file mode 100644 index 00000000..4b257726 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_config.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: MIT + +__all__ = ["get_run_validators", "set_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` + instead. + """ + if not isinstance(run, bool): + msg = "'run' must be bool." + raise TypeError(msg) + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` + instead. + """ + return _run_validators diff --git a/.venv/lib/python3.11/site-packages/attr/_funcs.py b/.venv/lib/python3.11/site-packages/attr/_funcs.py new file mode 100644 index 00000000..1adb5002 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_funcs.py @@ -0,0 +1,497 @@ +# SPDX-License-Identifier: MIT + + +import copy + +from ._compat import get_generic_base +from ._make import _OBJ_SETATTR, NOTHING, fields +from .exceptions import AttrsAttributeNotFoundError + + +_ATOMIC_TYPES = frozenset( + { + type(None), + bool, + int, + float, + str, + complex, + bytes, + type(...), + type, + range, + property, + } +) + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the *attrs* attribute values of *inst* as a dict. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + dict_factory (~typing.Callable): + A callable to produce dictionaries from. For example, to produce + ordered dictionaries instead of normal Python dictionaries, pass in + ``collections.OrderedDict``. + + retain_collection_types (bool): + Do not convert to `list` when encountering an attribute whose type + is `tuple` or `set`. Only meaningful if *recurse* is `True`. + + value_serializer (typing.Callable | None): + A hook that is called for every attribute or dict key/value. It + receives the current instance, field and value and must return the + (updated) value. The hook is run *after* the optional *filter* has + been applied. + + Returns: + Return type of *dict_factory*. + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + .. versionadded:: 21.3.0 + If a dict has a collection for a key, it is serialized as a tuple. + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + value_type = type(v) + if value_type in _ATOMIC_TYPES: + rv[a.name] = v + elif has(value_type): + rv[a.name] = asdict( + v, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = value_type if retain_collection_types is True else list + items = [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in v + ] + try: + rv[a.name] = cf(items) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv[a.name] = cf(*items) + elif issubclass(value_type, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in v.items() + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + is_key, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + val_type = type(val) + if val_type in _ATOMIC_TYPES: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + elif getattr(val_type, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(val_type, (tuple, list, set, frozenset)): + if retain_collection_types is True: + cf = val.__class__ + elif is_key: + cf = tuple + else: + cf = list + + rv = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in val + ] + ) + elif issubclass(val_type, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in val.items() + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the *attrs* attribute values of *inst* as a tuple. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): + Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + tuple_factory (~typing.Callable): + A callable to produce tuples from. For example, to produce lists + instead of tuples. + + retain_collection_types (bool): + Do not convert to `list` or `dict` when encountering an attribute + which type is `tuple`, `dict` or `set`. Only meaningful if + *recurse* is `True`. + + Returns: + Return type of *tuple_factory* + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + value_type = type(v) + if recurse is True: + if value_type in _ATOMIC_TYPES: + rv.append(v) + elif has(value_type): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + items = [ + ( + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + ) + for j in v + ] + try: + rv.append(cf(items)) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv.append(cf(*items)) + elif issubclass(value_type, dict): + df = value_type if retain is True else dict + rv.append( + df( + ( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk + ), + ( + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv + ), + ) + for kk, vv in v.items() + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with *attrs* attributes. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + Returns: + bool: + """ + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is not None: + return True + + # No attrs, maybe it's a specialized generic (A[str])? + generic_base = get_generic_base(cls) + if generic_base is not None: + generic_attrs = getattr(generic_base, "__attrs_attrs__", None) + if generic_attrs is not None: + # Stick it on here for speed next time. + cls.__attrs_attrs__ = generic_attrs + return generic_attrs is not None + return False + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + This is different from `evolve` that applies the changes to the arguments + that create the new instance. + + `evolve`'s behavior is preferable, but there are `edge cases`_ where it + doesn't work. Therefore `assoc` is deprecated, but will not be removed. + + .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251 + + Args: + inst: Instance of a class with *attrs* attributes. + + changes: Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + attrs.exceptions.AttrsAttributeNotFoundError: + If *attr_name* couldn't be found on *cls*. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. deprecated:: 17.1.0 + Use `attrs.evolve` instead if you can. This function will not be + removed du to the slightly different approach compared to + `attrs.evolve`, though. + """ + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in changes.items(): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + msg = f"{k} is not an attrs attribute on {new.__class__}." + raise AttrsAttributeNotFoundError(msg) + _OBJ_SETATTR(new, k, v) + return new + + +def resolve_types( + cls, globalns=None, localns=None, attribs=None, include_extras=True +): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in :class:`Attribute`'s + *type* field. In other words, you don't need to resolve your types if you + only use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, for example, if the name only + exists inside a method, you may pass *globalns* or *localns* to specify + other dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + Args: + cls (type): Class to resolve. + + globalns (dict | None): Dictionary containing global variables. + + localns (dict | None): Dictionary containing local variables. + + attribs (list | None): + List of attribs for the given class. This is necessary when calling + from inside a ``field_transformer`` since *cls* is not an *attrs* + class yet. + + include_extras (bool): + Resolve more accurately, if possible. Pass ``include_extras`` to + ``typing.get_hints``, if supported by the typing module. On + supported Python versions (3.9+), this resolves the types more + accurately. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class and you didn't pass any attribs. + + NameError: If types cannot be resolved because of missing variables. + + Returns: + *cls* so you can use this function also as a class decorator. Please + note that you have to apply it **after** `attrs.define`. That means the + decorator has to come in the line **before** `attrs.define`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + .. versionadded:: 23.1.0 *include_extras* + """ + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + if getattr(cls, "__attrs_types_resolved__", None) != cls: + import typing + + kwargs = { + "globalns": globalns, + "localns": localns, + "include_extras": include_extras, + } + + hints = typing.get_type_hints(cls, **kwargs) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _OBJ_SETATTR(field, "type", hints[field.name]) + # We store the class we resolved so that subclasses know they haven't + # been resolved. + cls.__attrs_types_resolved__ = cls + + # Return the class so you can use it as a decorator too. + return cls diff --git a/.venv/lib/python3.11/site-packages/attr/_make.py b/.venv/lib/python3.11/site-packages/attr/_make.py new file mode 100644 index 00000000..d24d9ba9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_make.py @@ -0,0 +1,3362 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +import abc +import contextlib +import copy +import enum +import inspect +import itertools +import linecache +import sys +import types +import unicodedata +import weakref + +from collections.abc import Callable, Mapping +from functools import cached_property +from typing import Any, NamedTuple, TypeVar + +# We need to import _compat itself in addition to the _compat members to avoid +# having the thread-local in the globals here. +from . import _compat, _config, setters +from ._compat import ( + PY_3_10_PLUS, + PY_3_11_PLUS, + PY_3_13_PLUS, + _AnnotationExtractor, + _get_annotations, + get_generic_base, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + UnannotatedAttributeError, +) + + +# This is used at least twice, so cache it here. +_OBJ_SETATTR = object.__setattr__ +_INIT_FACTORY_PAT = "__attr_factory_%s" +_CLASSVAR_PREFIXES = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_HASH_CACHE_FIELD = "_attrs_cached_hash" + +_EMPTY_METADATA_SINGLETON = types.MappingProxyType({}) + +# Unique object for unequivocal getattr() defaults. +_SENTINEL = object() + +_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate) + + +class _Nothing(enum.Enum): + """ + Sentinel to indicate the lack of a value when `None` is ambiguous. + + If extending attrs, you can use ``typing.Literal[NOTHING]`` to show + that a value may be ``NOTHING``. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. + """ + + NOTHING = enum.auto() + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + +NOTHING = _Nothing.NOTHING +""" +Sentinel to indicate the lack of a value when `None` is ambiguous. + +When using in 3rd party code, use `attrs.NothingType` for type annotations. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since `None` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new field / attribute on a class. + + Identical to `attrs.field`, except it's not keyword-only. + + Consider using `attrs.field` in new code (``attr.ib`` will *never* go away, + though). + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attr.s` (or similar)! + + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is `None` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 + *converter* as a replacement for the deprecated *convert* to achieve + consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 22.2.0 *alias* + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + if factory is not None: + if default is not NOTHING: + msg = ( + "The `default` and `factory` arguments are mutually exclusive." + ) + raise ValueError(msg) + if not callable(factory): + msg = "The `factory` argument must be a callable." + raise ValueError(msg) + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + alias=alias, + ) + + +def _compile_and_eval( + script: str, + globs: dict[str, Any] | None, + locs: Mapping[str, object] | None = None, + filename: str = "", +) -> None: + """ + Evaluate the script with the given global (globs) and local (locs) + variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _linecache_and_compile( + script: str, + filename: str, + globs: dict[str, Any] | None, + locals: Mapping[str, object] | None = None, +) -> dict[str, Any]: + """ + Cache the script with _linecache_, compile it and return the _locals_. + """ + + locs = {} if locals is None else locals + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + + filename = f"{base_filename[:-1]}-{count}>" + count += 1 + + _compile_and_eval(script, globs, locs, filename) + + return locs + + +def _make_attr_tuple_class(cls_name: str, attr_names: list[str]) -> type: + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = f"{cls_name}Attributes" + body = {} + for i, attr_name in enumerate(attr_names): + + def getter(self, i=i): + return self[i] + + body[attr_name] = property(getter) + return type(attr_class_name, (tuple,), body) + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +class _Attributes(NamedTuple): + attrs: type + base_attrs: list[Attribute] + base_attrs_map: dict[str, type] + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_CLASSVAR_PREFIXES) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + """ + return attrib_name in cls.__dict__ + + +def _collect_base_attrs( + cls, taken_attr_names +) -> tuple[list[Attribute], dict[str, type]]: + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, +) -> _Attributes: + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + If *collect_by_mro* is True, collect them in the correct MRO order, + otherwise use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = list(these.items()) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if a.__class__ is not _CountingAttr: + a = attrib(a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if unannotated: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + ), + key=lambda e: e[1].counter, + ) + + fca = Attribute.from_counting_attr + no = ClassProps.KeywordOnly.NO + own_attrs = [ + fca( + attr_name, + ca, + kw_only is not no, + anns.get(attr_name), + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + if kw_only is ClassProps.KeywordOnly.FORCE: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = base_attrs + own_attrs + + if field_transformer is not None: + attrs = tuple(field_transformer(cls, attrs)) + + # Check attr order after executing the field_transformer. + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" + raise ValueError(msg) + + if had_default is False and a.default is not NOTHING: + had_default = True + + # Resolve default field alias after executing field_transformer. + # This allows field_transformer to differentiate between explicit vs + # default aliases and supply their own defaults. + for a in attrs: + if not a.alias: + # Evolve is very slow, so we hold our nose and do it dirty. + _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) + + # Create AttrsClass *after* applying the field_transformer since it may + # add or remove attributes! + attr_names = [a.name for a in attrs] + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + return _Attributes(AttrsClass(attrs), base_attrs, base_attr_map) + + +def _make_cached_property_getattr(cached_properties, original_getattr, cls): + lines = [ + # Wrapped to get `__class__` into closure cell for super() + # (It will be replaced with the newly constructed class after construction). + "def wrapper(_cls):", + " __class__ = _cls", + " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", + " func = cached_properties.get(item)", + " if func is not None:", + " result = func(self)", + " _setter = _cached_setattr_get(self)", + " _setter(item, result)", + " return result", + ] + if original_getattr is not None: + lines.append( + " return original_getattr(self, item)", + ) + else: + lines.extend( + [ + " try:", + " return super().__getattribute__(item)", + " except AttributeError:", + " if not hasattr(super(), '__getattr__'):", + " raise", + " return super().__getattr__(item)", + " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", + " raise AttributeError(original_error)", + ] + ) + + lines.extend( + [ + " return __getattr__", + "__getattr__ = wrapper(_cls)", + ] + ) + + unique_filename = _generate_unique_filename(cls, "getattr") + + glob = { + "cached_properties": cached_properties, + "_cached_setattr_get": _OBJ_SETATTR.__get__, + "original_getattr": original_getattr, + } + + return _linecache_and_compile( + "\n".join(lines), unique_filename, glob, locals={"_cls": cls} + )["__getattr__"] + + +def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + "__traceback__", + "__suppress_context__", + "__notes__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + if isinstance(self, BaseException) and name in ("__notes__",): + BaseException.__delattr__(self, name) + return + + raise FrozenInstanceError + + +def evolve(*args, **changes): + """ + Create a new instance, based on the first positional argument with + *changes* applied. + + .. tip:: + + On Python 3.13 and later, you can also use `copy.replace` instead. + + Args: + + inst: + Instance of a class with *attrs* attributes. *inst* must be passed + as a positional argument. + + changes: + Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + TypeError: + If *attr_name* couldn't be found in the class ``__init__``. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 17.1.0 + .. deprecated:: 23.1.0 + It is now deprecated to pass the instance using the keyword argument + *inst*. It will raise a warning until at least April 2024, after which + it will become an error. Always pass the instance as a positional + argument. + .. versionchanged:: 24.1.0 + *inst* can't be passed as a keyword argument anymore. + """ + try: + (inst,) = args + except ValueError: + msg = ( + f"evolve() takes 1 positional argument, but {len(args)} were given" + ) + raise TypeError(msg) from None + + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = a.alias + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +class _ClassBuilder: + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_add_method_dunders", + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_custom_setattr", + "_has_post_init", + "_has_pre_init", + "_is_exc", + "_on_setattr", + "_pre_init_has_args", + "_repr_added", + "_script_snippets", + "_slots", + "_weakref_slot", + "_wrote_own_setattr", + ) + + def __init__( + self, + cls: type, + these, + auto_attribs: bool, + props: ClassProps, + has_custom_setattr: bool, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + props.kw_only, + props.collected_fields_by_mro, + props.field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if props.is_slotted else {} + self._attrs = attrs + self._base_names = {a.name for a in base_attrs} + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = props.is_slotted + self._frozen = props.is_frozen + self._weakref_slot = props.has_weakref_slot + self._cache_hash = ( + props.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._pre_init_has_args = False + if self._has_pre_init: + # Check if the pre init method has more arguments than just `self` + # We want to pass arguments if pre init expects arguments + pre_init_func = cls.__attrs_pre_init__ + pre_init_signature = inspect.signature(pre_init_func) + self._pre_init_has_args = len(pre_init_signature.parameters) > 1 + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = props.is_exception + self._on_setattr = props.on_setattr_hook + + self._has_custom_setattr = has_custom_setattr + self._wrote_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + self._cls_dict["__attrs_props__"] = props + + if props.is_frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._wrote_own_setattr = True + elif self._on_setattr in ( + _DEFAULT_ON_SETATTR, + setters.validate, + setters.convert, + ): + has_validator = has_converter = False + for a in attrs: + if a.validator is not None: + has_validator = True + if a.converter is not None: + has_converter = True + + if has_validator and has_converter: + break + if ( + ( + self._on_setattr == _DEFAULT_ON_SETATTR + and not (has_validator or has_converter) + ) + or (self._on_setattr == setters.validate and not has_validator) + or (self._on_setattr == setters.convert and not has_converter) + ): + # If class-level on_setattr is set to convert + validate, but + # there's no field to convert or validate, pretend like there's + # no on_setattr. + self._on_setattr = None + + if props.added_pickling: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + # tuples of script, globs, hook + self._script_snippets: list[ + tuple[str, dict, Callable[[dict, dict], Any]] + ] = [] + self._repr_added = False + + # We want to only do this check once; in 99.9% of cases these + # exist. + if not hasattr(self._cls, "__module__") or not hasattr( + self._cls, "__qualname__" + ): + self._add_method_dunders = self._add_method_dunders_safe + else: + self._add_method_dunders = self._add_method_dunders_unsafe + + def __repr__(self): + return f"<_ClassBuilder(cls={self._cls.__name__})>" + + def _eval_snippets(self) -> None: + """ + Evaluate any registered snippets in one go. + """ + script = "\n".join([snippet[0] for snippet in self._script_snippets]) + globs = {} + for _, snippet_globs, _ in self._script_snippets: + globs.update(snippet_globs) + + locs = _linecache_and_compile( + script, + _generate_unique_filename(self._cls, "methods"), + globs, + ) + + for _, _, hook in self._script_snippets: + hook(self._cls_dict, locs) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + self._eval_snippets() + if self._slots is True: + cls = self._create_slots_class() + self._cls.__attrs_base_of_slotted__ = weakref.ref(cls) + else: + cls = self._patch_original_class() + if PY_3_10_PLUS: + cls = abc.update_abstractmethods(cls) + + # The method gets only called if it's not inherited from a base class. + # _has_own_attribute does NOT work properly for classmethods. + if ( + getattr(cls, "__attrs_init_subclass__", None) + and "__attrs_init_subclass__" not in cls.__dict__ + ): + cls.__attrs_init_subclass__() + + return cls + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _SENTINEL) is not _SENTINEL + ): + # An AttributeError can happen if a base class defines a + # class variable and we want to set an attribute with the + # same name by using only a type annotation. + with contextlib.suppress(AttributeError): + delattr(cls, name) + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._wrote_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = _OBJ_SETATTR + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in self._cls_dict.items() + if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") + } + + # 3.14.0rc2+ + if hasattr(sys, "_clear_type_descriptors"): + sys._clear_type_descriptors(self._cls) + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._wrote_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = _OBJ_SETATTR + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = {} + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + cached_properties = { + name: cached_prop.func + for name, cached_prop in cd.items() + if isinstance(cached_prop, cached_property) + } + + # Collect methods with a `__class__` reference that are shadowed in the new class. + # To know to update them. + additional_closure_functions_to_update = [] + if cached_properties: + class_annotations = _get_annotations(self._cls) + for name, func in cached_properties.items(): + # Add cached properties to names for slotting. + names += (name,) + # Clear out function from class to avoid clashing. + del cd[name] + additional_closure_functions_to_update.append(func) + annotation = inspect.signature(func).return_annotation + if annotation is not inspect.Parameter.empty: + class_annotations[name] = annotation + + original_getattr = cd.get("__getattr__") + if original_getattr is not None: + additional_closure_functions_to_update.append(original_getattr) + + cd["__getattr__"] = _make_cached_property_getattr( + cached_properties, original_getattr, self._cls + ) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overridden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in existing_slots.items() + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_HASH_CACHE_FIELD) + + cd["__slots__"] = tuple(slot_names) + + cd["__qualname__"] = self._cls.__qualname__ + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # . + # If a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in itertools.chain( + cls.__dict__.values(), additional_closure_functions_to_update + ): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # noqa: PERF203 + # ValueError: Cell is empty + pass + else: + if match: + cell.cell_contents = cls + return cls + + def add_repr(self, ns): + script, globs = _make_repr_script(self._attrs, ns) + + def _attach_repr(cls_dict, globs): + cls_dict["__repr__"] = self._add_method_dunders(globs["__repr__"]) + + self._script_snippets.append((script, globs, _attach_repr)) + self._repr_added = True + return self + + def add_str(self): + if not self._repr_added: + msg = "__str__ can only be generated if a __repr__ exists." + raise ValueError(msg) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return {name: getattr(self, name) for name in state_attr_names} + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _OBJ_SETATTR.__get__(self) + if isinstance(state, tuple): + # Backward compatibility with attrs instances pickled with + # attrs versions before v22.2.0 which stored tuples. + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + else: + for name in state_attr_names: + if name in state: + __bound_setattr(name, state[name]) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_HASH_CACHE_FIELD, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + script, globs = _make_hash_script( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + + def attach_hash(cls_dict: dict, locs: dict) -> None: + cls_dict["__hash__"] = self._add_method_dunders(locs["__hash__"]) + + self._script_snippets.append((script, globs, attach_hash)) + + return self + + def add_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=False, + ) + + def _attach_init(cls_dict, globs): + init = globs["__init__"] + init.__annotations__ = annotations + cls_dict["__init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_init)) + + return self + + def add_replace(self): + self._cls_dict["__replace__"] = self._add_method_dunders( + lambda self, **changes: evolve(self, **changes) + ) + return self + + def add_match_args(self): + self._cls_dict["__match_args__"] = tuple( + field.name + for field in self._attrs + if field.init and not field.kw_only + ) + + def add_attrs_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=True, + ) + + def _attach_attrs_init(cls_dict, globs): + init = globs["__attrs_init__"] + init.__annotations__ = annotations + cls_dict["__attrs_init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_attrs_init)) + + return self + + def add_eq(self): + cd = self._cls_dict + + script, globs = _make_eq_script(self._attrs) + + def _attach_eq(cls_dict, globs): + cls_dict["__eq__"] = self._add_method_dunders(globs["__eq__"]) + + self._script_snippets.append((script, globs, _attach_eq)) + + cd["__ne__"] = __ne__ + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + msg = "Can't combine custom __setattr__ with on_setattr hooks." + raise ValueError(msg) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _OBJ_SETATTR(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._wrote_own_setattr = True + + return self + + def _add_method_dunders_unsafe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method*. + """ + method.__module__ = self._cls.__module__ + + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + method.__doc__ = ( + f"Method generated by attrs for class {self._cls.__qualname__}." + ) + + return method + + def _add_method_dunders_safe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + with contextlib.suppress(AttributeError): + method.__module__ = self._cls.__module__ + + with contextlib.suppress(AttributeError): + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + with contextlib.suppress(AttributeError): + method.__doc__ = f"Method generated by attrs for class {self._cls.__qualname__}." + + return method + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + unsafe_hash=None, + force_kw_only=True, +): + r""" + A class decorator that adds :term:`dunder methods` according to the + specified attributes using `attr.ib` or the *these* argument. + + Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will + *never* go away, though). + + Args: + repr_ns (str): + When using nested classes, there was no way in Python 2 to + automatically detect that. This argument allows to set a custom + name for a more meaningful ``repr`` output. This argument is + pointless in Python 3 and is therefore deprecated. + + .. caution:: + Refer to `attrs.define` for the rest of the parameters, but note that they + can have different defaults. + + Notably, leaving *on_setattr* as `None` will **not** add any hooks. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports `None` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 21.3.0 *match_args* + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. deprecated:: 24.1.0 *repr_ns* + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 *force_kw_only* + """ + if repr_ns is not None: + import warnings + + warnings.warn( + DeprecationWarning( + "The `repr_ns` argument is deprecated and will be removed in or after August 2025." + ), + stacklevel=2, + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + + # unsafe_hash takes precedence due to PEP 681. + if unsafe_hash is not None: + hash = unsafe_hash + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + nonlocal hash + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + msg = "Can't freeze a class with a custom __setattr__." + raise ValueError(msg) + + eq = not is_exc and _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + + Hashability = ClassProps.Hashability + + if is_exc: + hashability = Hashability.LEAVE_ALONE + elif hash is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif hash is False: + hashability = Hashability.LEAVE_ALONE + elif hash is None: + if auto_detect is True and _has_own_attribute(cls, "__hash__"): + hashability = Hashability.LEAVE_ALONE + elif eq is True and is_frozen is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif eq is False: + hashability = Hashability.LEAVE_ALONE + else: + hashability = Hashability.UNHASHABLE + else: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + KeywordOnly = ClassProps.KeywordOnly + if kw_only: + kwo = KeywordOnly.FORCE if force_kw_only else KeywordOnly.YES + else: + kwo = KeywordOnly.NO + + props = ClassProps( + is_exception=is_exc, + is_frozen=is_frozen, + is_slotted=slots, + collected_fields_by_mro=collect_by_mro, + added_init=_determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ), + added_repr=_determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ), + added_eq=eq, + added_ordering=not is_exc + and _determine_whether_to_implement( + cls, + order_, + auto_detect, + ("__lt__", "__le__", "__gt__", "__ge__"), + ), + hashability=hashability, + added_match_args=match_args, + kw_only=kwo, + has_weakref_slot=weakref_slot, + added_str=str, + added_pickling=_determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + on_setattr_hook=on_setattr, + field_transformer=field_transformer, + ) + + if not props.is_hashable and cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." + raise TypeError(msg) + + builder = _ClassBuilder( + cls, + these, + auto_attribs=auto_attribs, + props=props, + has_custom_setattr=has_own_setattr, + ) + + if props.added_repr: + builder.add_repr(repr_ns) + + if props.added_str: + builder.add_str() + + if props.added_eq: + builder.add_eq() + if props.added_ordering: + builder.add_order() + + if not frozen: + builder.add_setattr() + + if props.is_hashable: + builder.add_hash() + elif props.hashability is Hashability.UNHASHABLE: + builder.make_unhashable() + + if props.added_init: + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, init must be True." + raise TypeError(msg) + + if PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"): + builder.add_replace() + + if ( + PY_3_10_PLUS + and match_args + and not _has_own_attribute(cls, "__match_args__") + ): + builder.add_match_args() + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ is _frozen_setattrs + + +def _generate_unique_filename(cls: type, func_name: str) -> str: + """ + Create a "filename" suitable for a function being generated. + """ + return ( + f"" + ) + + +def _make_hash_script( + cls: type, attrs: list[Attribute], frozen: bool, cache_hash: bool +) -> tuple[str, dict]: + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + type_hash = hash(_generate_unique_filename(cls, "hash")) + # If eq is custom generated, we need to include the functions in globs + globs = {} + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + hash_def += ", *" + + hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + f" {type_hash},", + ] + ) + + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + globs[cmp_name] = a.eq_key + method_lines.append( + indent + f" {cmp_name}(self.{a.name})," + ) + else: + method_lines.append(indent + f" self.{a.name},") + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + f"if self.{_HASH_CACHE_FIELD} is None:") + if frozen: + append_hash_computation_lines( + f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + f"self.{_HASH_CACHE_FIELD} = ", tab * 2 + ) + method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}") + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return script, globs + + +def _add_hash(cls: type, attrs: list[Attribute]): + """ + Add a hash method to *cls*. + """ + script, globs = _make_hash_script( + cls, attrs, frozen=False, cache_hash=False + ) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__hash__") + ) + cls.__hash__ = globs["__hash__"] + return cls + + +def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + +def _make_eq_script(attrs: list) -> tuple[str, dict]: + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + globs = {} + if attrs: + lines.append(" return (") + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" + ) + else: + lines.append(f" self.{a.name} == other.{a.name}") + if a is not attrs[-1]: + lines[-1] = f"{lines[-1]} and" + lines.append(" )") + else: + lines.append(" return True") + + script = "\n".join(lines) + + return script, globs + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_eq_script(attrs) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__eq__") + ) + cls.__eq__ = globs["__eq__"] + cls.__ne__ = __ne__ + + return cls + + +def _make_repr_script(attrs, ns) -> tuple[str, dict]: + """ + Create the source and globs for a __repr__ and return it. + """ + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, (repr if a.repr is True else a.repr), a.init) + for a in attrs + if a.repr is not False + ) + globs = { + name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr + } + globs["_compat"] = _compat + globs["AttributeError"] = AttributeError + globs["NOTHING"] = NOTHING + attribute_fragments = [] + for name, r, i in attr_names_with_reprs: + accessor = ( + "self." + name if i else 'getattr(self, "' + name + '", NOTHING)' + ) + fragment = ( + "%s={%s!r}" % (name, accessor) + if r == repr + else "%s={%s_repr(%s)}" % (name, name, accessor) + ) + attribute_fragments.append(fragment) + repr_fragment = ", ".join(attribute_fragments) + + if ns is None: + cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' + else: + cls_name_fragment = ns + ".{self.__class__.__name__}" + + lines = [ + "def __repr__(self):", + " try:", + " already_repring = _compat.repr_context.already_repring", + " except AttributeError:", + " already_repring = {id(self),}", + " _compat.repr_context.already_repring = already_repring", + " else:", + " if id(self) in already_repring:", + " return '...'", + " else:", + " already_repring.add(id(self))", + " try:", + f" return f'{cls_name_fragment}({repr_fragment})'", + " finally:", + " already_repring.remove(id(self))", + ] + + return "\n".join(lines), globs + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_repr_script(attrs, ns) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__repr__") + ) + cls.__repr__ = globs["__repr__"] + return cls + + +def fields(cls): + """ + Return the tuple of *attrs* attributes for a class. + + The tuple also allows accessing the fields by their names (see below for + examples). + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + tuple (with name accessors) of `attrs.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + .. versionchanged:: 23.1.0 Add support for generic classes. + """ + generic_base = get_generic_base(cls) + + if generic_base is None and not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + + attrs = getattr(cls, "__attrs_attrs__", None) + + if attrs is None: + if generic_base is not None: + attrs = getattr(generic_base, "__attrs_attrs__", None) + if attrs is not None: + # Even though this is global state, stick it on here to speed + # it up. We rely on `cls` being cached for this to be + # efficient. + cls.__attrs_attrs__ = attrs + return attrs + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of *attrs* attributes for a class, whose keys + are the attribute names. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + dict[str, attrs.Attribute]: Dict of attribute name to definition + + .. versionadded:: 18.1.0 + """ + if not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + return {a.name: a for a in attrs} + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + Args: + inst: Instance of a class with *attrs* attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + cls = base_attr_map.get(a_name) + return cls and "__slots__" in cls.__dict__ + + +def _make_init_script( + cls, + attrs, + pre_init, + pre_init_has_args, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + cls_on_setattr, + attrs_init, +) -> tuple[str, dict, dict]: + has_cls_on_setattr = ( + cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP + ) + + if frozen and has_cls_on_setattr: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = True + elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: + needs_cached_setattr = True + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + pre_init_has_args, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + "__attrs_init__" if attrs_init else "__init__", + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__ + + return script, globs, annotations + + +def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return f"_setattr('{attr_name}', {value_var})" + + +def _setattr_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})" + + +def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return f"self.{attr_name} = {value}" + + +def _assign_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True, converter) + + return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}" + + +def _determine_setters( + frozen: bool, slots: bool, base_attr_map: dict[str, type] +): + """ + Determine the correct setter functions based on whether a class is frozen + and/or slotted. + """ + if frozen is True: + if slots is True: + return (), _setattr, _setattr_with_converter + + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + + def fmt_setter( + attr_name: str, value_var: str, has_on_setattr: bool + ) -> str: + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return f"_inst_dict['{attr_name}'] = {value_var}" + + def fmt_setter_with_converter( + attr_name: str, + value_var: str, + has_on_setattr: bool, + converter: Converter, + ) -> str: + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr, converter + ) + + return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}" + + return ( + ("_inst_dict = self.__dict__",), + fmt_setter, + fmt_setter_with_converter, + ) + + # Not frozen -- we can just assign directly. + return (), _assign, _assign_with_converter + + +def _attrs_to_init_script( + attrs: list[Attribute], + is_frozen: bool, + is_slotted: bool, + call_pre_init: bool, + pre_init_has_args: bool, + call_post_init: bool, + does_cache_hash: bool, + base_attr_map: dict[str, type], + is_exc: bool, + needs_cached_setattr: bool, + has_cls_on_setattr: bool, + method_name: str, +) -> tuple[str, dict, dict]: + """ + Return a script of an initializer for *attrs*, a dict of globals, and + annotations for the initializer. + + The globals are required by the generated script. + """ + lines = ["self.__attrs_pre_init__()"] if call_pre_init else [] + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. Note _setattr will be used again below if + # does_cache_hash is True. + "_setattr = _cached_setattr_get(self)" + ) + + extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters( + is_frozen, is_slotted, base_attr_map + ) + lines.extend(extra_lines) + + args = [] # Parameters in the definition of __init__ + pre_init_args = [] # Parameters in the call to __attrs_pre_init__ + kw_only_args = [] # Used for both 'args' and 'pre_init_args' above + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_cls_on_setattr + ) + # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not + # explicitly provided + arg_name = a.alias + + has_factory = isinstance(a.default, Factory) + maybe_self = "self" if has_factory and a.default.takes_self else "" + + if a.converter is not None and not isinstance(a.converter, Converter): + converter = Converter(a.converter) + else: + converter = a.converter + + if a.init is False: + if has_factory: + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + elif converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = f"{arg_name}=attr_dict['{attr_name}'].default" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = f"{arg_name}=NOTHING" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + lines.append(f"if {arg_name} is not NOTHING:") + + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and converter is None: + annotations[arg_name] = a.type + elif converter is not None and converter._first_param_type: + # Use the type from the converter if present. + annotations[arg_name] = converter._first_param_type + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append(f" {val_name}(self, {attr_name}, self.{a.name})") + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if call_post_init: + lines.append("self.__attrs_post_init__()") + + # Because this is set only after __attrs_post_init__ is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to field + # values during post-init combined with post-init accessing the hash code + # would result in silent bugs. + if does_cache_hash: + if is_frozen: + if is_slotted: + init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)" + else: + init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None" + else: + init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None" + lines.append(init_hash_cache) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join(f"self.{a.name}" for a in attrs if a.init) + + lines.append(f"BaseException.__init__(self, {vals})") + + args = ", ".join(args) + pre_init_args = ", ".join(pre_init_args) + if kw_only_args: + # leading comma & kw_only args + args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}" + pre_init_kw_only_args = ", ".join( + [ + f"{kw_arg_name}={kw_arg_name}" + # We need to remove the defaults from the kw_only_args. + for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args) + ] + ) + pre_init_args += ", " if pre_init_args else "" + pre_init_args += pre_init_kw_only_args + + if call_pre_init and pre_init_has_args: + # If pre init method has arguments, pass the values given to __init__. + lines[0] = f"self.__attrs_pre_init__({pre_init_args})" + + # Python <3.12 doesn't allow backslashes in f-strings. + NL = "\n " + return ( + f"""def {method_name}(self, {args}): + {NL.join(lines) if lines else "pass"} +""", + names_for_globals, + annotations, + ) + + +def _default_init_alias_for(name: str) -> str: + """ + The default __init__ parameter name for a field. + + This performs private-name adjustment via leading-unscore stripping, + and is the default value of Attribute.alias if not provided. + """ + + return name.lstrip("_") + + +class Attribute: + """ + *Read-only* representation of an attribute. + + .. warning:: + + You should never instantiate this class yourself. + + The class has *all* arguments of `attr.ib` (except for ``factory`` which is + only syntactic sugar for ``default=Factory(...)`` plus the following: + + - ``name`` (`str`): The name of the attribute. + - ``alias`` (`str`): The __init__ parameter name of the attribute, after + any explicit overrides and default private-attribute-name handling. + - ``inherited`` (`bool`): Whether or not that attribute has been inherited + from a base class. + - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The + callables that are used for comparing and ordering objects by this + attribute, respectively. These are set by passing a callable to + `attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also + :ref:`comparison customization `. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The :ref:`field transformer ` hook receives a list of + them. + - The ``alias`` property exposes the __init__ parameter name of the field, + with any overrides and default private-attribute handling applied. + + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + .. versionadded:: 22.2.0 *alias* + + For the full version history of the fields, see `attr.ib`. + """ + + # These slots must NOT be reordered because we use them later for + # instantiation. + __slots__ = ( # noqa: RUF023 + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + "alias", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + alias=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _OBJ_SETATTR.__get__(self) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + types.MappingProxyType(dict(metadata)) # Shallow copy + if metadata + else _EMPTY_METADATA_SINGLETON + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + bound_setattr("alias", alias) + + def __setattr__(self, name, value): + raise FrozenInstanceError + + @classmethod + def from_counting_attr( + cls, name: str, ca: _CountingAttr, kw_only: bool, type=None + ): + # The 'kw_only' argument is the class-level setting, and is used if the + # attribute itself does not explicitly set 'kw_only'. + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + msg = f"Type annotation and type argument cannot both be present for '{name}'." + raise ValueError(msg) + return cls( + name, + ca._default, + ca._validator, + ca.repr, + None, + ca.hash, + ca.init, + False, + ca.metadata, + type, + ca.converter, + kw_only if ca.kw_only is None else ca.kw_only, + ca.eq, + ca.eq_key, + ca.order, + ca.order_key, + ca.on_setattr, + ca.alias, + ) + + # Don't use attrs.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attrs.evolve` but that function does not work + with :class:`attrs.Attribute`. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _OBJ_SETATTR.__get__(self) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + ( + types.MappingProxyType(dict(value)) + if value + else _EMPTY_METADATA_SINGLETON + ), + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr: + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "_default", + "_validator", + "alias", + "converter", + "counter", + "eq", + "eq_key", + "hash", + "init", + "kw_only", + "metadata", + "on_setattr", + "order", + "order_key", + "repr", + "type", + ) + __attrs_attrs__ = ( + *tuple( + Attribute( + name=name, + alias=_default_init_alias_for(name), + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + "alias", + ) + ), + Attribute( + name="metadata", + alias="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + alias, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + self.alias = alias + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + Raises: + DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class ClassProps: + """ + Effective class properties as derived from parameters to `attr.s()` or + `define()` decorators. + + This is the same data structure that *attrs* uses internally to decide how + to construct the final class. + + Warning: + + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + + Attributes: + is_exception (bool): + Whether the class is treated as an exception class. + + is_slotted (bool): + Whether the class is `slotted `. + + has_weakref_slot (bool): + Whether the class has a slot for weak references. + + is_frozen (bool): + Whether the class is frozen. + + kw_only (KeywordOnly): + Whether / how the class enforces keyword-only arguments on the + ``__init__`` method. + + collected_fields_by_mro (bool): + Whether the class fields were collected by method resolution order. + That is, correctly but unlike `dataclasses`. + + added_init (bool): + Whether the class has an *attrs*-generated ``__init__`` method. + + added_repr (bool): + Whether the class has an *attrs*-generated ``__repr__`` method. + + added_eq (bool): + Whether the class has *attrs*-generated equality methods. + + added_ordering (bool): + Whether the class has *attrs*-generated ordering methods. + + hashability (Hashability): How `hashable ` the class is. + + added_match_args (bool): + Whether the class supports positional `match ` over its + fields. + + added_str (bool): + Whether the class has an *attrs*-generated ``__str__`` method. + + added_pickling (bool): + Whether the class has *attrs*-generated ``__getstate__`` and + ``__setstate__`` methods for `pickle`. + + on_setattr_hook (Callable[[Any, Attribute[Any], Any], Any] | None): + The class's ``__setattr__`` hook. + + field_transformer (Callable[[Attribute[Any]], Attribute[Any]] | None): + The class's `field transformers `. + + .. versionadded:: 25.4.0 + """ + + class Hashability(enum.Enum): + """ + The hashability of a class. + + .. versionadded:: 25.4.0 + """ + + HASHABLE = "hashable" + """Write a ``__hash__``.""" + HASHABLE_CACHED = "hashable_cache" + """Write a ``__hash__`` and cache the hash.""" + UNHASHABLE = "unhashable" + """Set ``__hash__`` to ``None``.""" + LEAVE_ALONE = "leave_alone" + """Don't touch ``__hash__``.""" + + class KeywordOnly(enum.Enum): + """ + How attributes should be treated regarding keyword-only parameters. + + .. versionadded:: 25.4.0 + """ + + NO = "no" + """Attributes are not keyword-only.""" + YES = "yes" + """Attributes in current class without kw_only=False are keyword-only.""" + FORCE = "force" + """All attributes are keyword-only.""" + + __slots__ = ( # noqa: RUF023 -- order matters for __init__ + "is_exception", + "is_slotted", + "has_weakref_slot", + "is_frozen", + "kw_only", + "collected_fields_by_mro", + "added_init", + "added_repr", + "added_eq", + "added_ordering", + "hashability", + "added_match_args", + "added_str", + "added_pickling", + "on_setattr_hook", + "field_transformer", + ) + + def __init__( + self, + is_exception, + is_slotted, + has_weakref_slot, + is_frozen, + kw_only, + collected_fields_by_mro, + added_init, + added_repr, + added_eq, + added_ordering, + hashability, + added_match_args, + added_str, + added_pickling, + on_setattr_hook, + field_transformer, + ): + self.is_exception = is_exception + self.is_slotted = is_slotted + self.has_weakref_slot = has_weakref_slot + self.is_frozen = is_frozen + self.kw_only = kw_only + self.collected_fields_by_mro = collected_fields_by_mro + self.added_init = added_init + self.added_repr = added_repr + self.added_eq = added_eq + self.added_ordering = added_ordering + self.hashability = hashability + self.added_match_args = added_match_args + self.added_str = added_str + self.added_pickling = added_pickling + self.on_setattr_hook = on_setattr_hook + self.field_transformer = field_transformer + + @property + def is_hashable(self): + return ( + self.hashability is ClassProps.Hashability.HASHABLE + or self.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + + +_cas = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in ClassProps.__slots__ +] + +ClassProps = _add_eq(_add_repr(ClassProps, attrs=_cas), attrs=_cas) + + +class Factory: + """ + Stores a factory callable. + + If passed as the default value to `attrs.field`, the factory is used to + generate a new value. + + Args: + factory (typing.Callable): + A callable that takes either none or exactly one mandatory + positional argument depending on *takes_self*. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +class Converter: + """ + Stores a converter callable. + + Allows for the wrapped converter to take additional arguments. The + arguments are passed in the order they are documented. + + Args: + converter (Callable): A callable that converts the passed value. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. (default: `False`) + + takes_field (bool): + Pass the field definition (an :class:`Attribute`) into the + converter as a positional argument. (default: `False`) + + .. versionadded:: 24.1.0 + """ + + __slots__ = ( + "__call__", + "_first_param_type", + "_global_name", + "converter", + "takes_field", + "takes_self", + ) + + def __init__(self, converter, *, takes_self=False, takes_field=False): + self.converter = converter + self.takes_self = takes_self + self.takes_field = takes_field + + ex = _AnnotationExtractor(converter) + self._first_param_type = ex.get_first_param_type() + + if not (self.takes_self or self.takes_field): + self.__call__ = lambda value, _, __: self.converter(value) + elif self.takes_self and not self.takes_field: + self.__call__ = lambda value, instance, __: self.converter( + value, instance + ) + elif not self.takes_self and self.takes_field: + self.__call__ = lambda value, __, field: self.converter( + value, field + ) + else: + self.__call__ = lambda value, instance, field: self.converter( + value, instance, field + ) + + rt = ex.get_return_type() + if rt is not None: + self.__call__.__annotations__["return"] = rt + + @staticmethod + def _get_global_name(attr_name: str) -> str: + """ + Return the name that a converter for an attribute name *attr_name* + would have. + """ + return f"__attr_converter_{attr_name}" + + def _fmt_converter_call(self, attr_name: str, value_var: str) -> str: + """ + Return a string that calls the converter for an attribute name + *attr_name* and the value in variable named *value_var* according to + `self.takes_self` and `self.takes_field`. + """ + if not (self.takes_self or self.takes_field): + return f"{self._get_global_name(attr_name)}({value_var})" + + if self.takes_self and self.takes_field: + return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])" + + if self.takes_self: + return f"{self._get_global_name(attr_name)}({value_var}, self)" + + return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])" + + def __getstate__(self): + """ + Return a dict containing only converter and takes_self -- the rest gets + computed when loading. + """ + return { + "converter": self.converter, + "takes_self": self.takes_self, + "takes_field": self.takes_field, + } + + def __setstate__(self, state): + """ + Load instance from state. + """ + self.__init__(**state) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in ("converter", "takes_self", "takes_field") +] + +Converter = _add_hash( + _add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f +) + + +def make_class( + name, attrs, bases=(object,), class_body=None, **attributes_arguments +): + r""" + A quick way to create a new class called *name* with *attrs*. + + .. note:: + + ``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define` + which means that it doesn't come with some of the improved defaults. + + For example, if you want the same ``on_setattr`` behavior as in + `attrs.define`, you have to pass the hooks yourself: ``make_class(..., + on_setattr=setters.pipe(setters.convert, setters.validate)`` + + .. warning:: + + It is *your* duty to ensure that the class name and the attribute names + are valid identifiers. ``make_class()`` will *not* validate them for + you. + + Args: + name (str): The name for the new class. + + attrs (list | dict): + A list of names or a dictionary of mappings of names to `attr.ib`\ + s / `attrs.field`\ s. + + The order is deduced from the order of the names or attributes + inside *attrs*. Otherwise the order of the definition of the + attributes is used. + + bases (tuple[type, ...]): Classes that the new class will subclass. + + class_body (dict): + An optional dictionary of class attributes for the new class. + + attributes_arguments: Passed unmodified to `attr.s`. + + Returns: + type: A new class with *attrs*. + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + .. versionchanged:: 23.2.0 *class_body* + .. versionchanged:: 25.2.0 Class names can now be unicode. + """ + # Class identifiers are converted into the normal form NFKC while parsing + name = unicodedata.normalize("NFKC", name) + + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = {a: attrib() for a in attrs} + else: + msg = "attrs argument must be a dict or a list." + raise TypeError(msg) + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if class_body is not None: + body.update(class_body) + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + with contextlib.suppress(AttributeError, ValueError): + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + cls = _attrs(these=cls_dict, **attributes_arguments)(type_) + # Only add type annotations now or "_attrs()" will complain: + cls.__annotations__ = { + k: v.type for k, v in cls_dict.items() if v.type is not None + } + return cls + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, unsafe_hash=True) +class _AndValidator: + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if they + have any. + + converters (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + return_instance = any(isinstance(c, Converter) for c in converters) + + if return_instance: + + def pipe_converter(val, inst, field): + for c in converters: + val = ( + c(val, inst, field) if isinstance(c, Converter) else c(val) + ) + + return val + + else: + + def pipe_converter(val): + for c in converters: + val = c(val) + + return val + + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = TypeVar("A") + pipe_converter.__annotations__.update({"val": A, "return": A}) + else: + # Get parameter type from first converter. + t = _AnnotationExtractor(converters[0]).get_first_param_type() + if t: + pipe_converter.__annotations__["val"] = t + + last = converters[-1] + if not PY_3_11_PLUS and isinstance(last, Converter): + last = last.__call__ + + # Get return type from last converter. + rt = _AnnotationExtractor(last).get_return_type() + if rt: + pipe_converter.__annotations__["return"] = rt + + if return_instance: + return Converter(pipe_converter, takes_self=True, takes_field=True) + return pipe_converter diff --git a/.venv/lib/python3.11/site-packages/attr/_next_gen.py b/.venv/lib/python3.11/site-packages/attr/_next_gen.py new file mode 100644 index 00000000..4ccd0da2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_next_gen.py @@ -0,0 +1,674 @@ +# SPDX-License-Identifier: MIT + +""" +These are keyword-only APIs that call `attr.s` and `attr.ib` with different +default values. +""" + +from functools import partial + +from . import setters +from ._funcs import asdict as _asdict +from ._funcs import astuple as _astuple +from ._make import ( + _DEFAULT_ON_SETATTR, + NOTHING, + _frozen_setattrs, + attrib, + attrs, +) +from .exceptions import NotAnAttrsClassError, UnannotatedAttributeError + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + unsafe_hash=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + force_kw_only=False, +): + r""" + A class decorator that adds :term:`dunder methods` according to + :term:`fields ` specified using :doc:`type annotations `, + `field()` calls, or the *these* argument. + + Since *attrs* patches or replaces an existing class, you cannot use + `object.__init_subclass__` with *attrs* classes, because it runs too early. + As a replacement, you can define ``__attrs_init_subclass__`` on your class. + It will be called by *attrs* classes that subclass it after they're + created. See also :ref:`init-subclass`. + + Args: + slots (bool): + Create a :term:`slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the + default dict classes, but have some gotchas you should know about, + so we encourage you to read the :term:`glossary entry `. + + auto_detect (bool): + Instead of setting the *init*, *repr*, *eq*, and *hash* arguments + explicitly, assume they are set to True **unless any** of the + involved methods for one of the arguments is implemented in the + *current* class (meaning, it is *not* inherited from some base + class). + + So, for example by implementing ``__eq__`` on a class yourself, + *attrs* will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a + sensible ``__ne__`` by default, so it *should* be enough to only + implement ``__eq__`` in most cases). + + Passing :data:`True` or :data:`False` to *init*, *repr*, *eq*, or *hash* + overrides whatever *auto_detect* would determine. + + auto_exc (bool): + If the class subclasses `BaseException` (which implicitly includes + any subclass of any exception), the following happens to behave + like a well-behaved Python exception class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids [#]_ , + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the + ``args`` attribute, + - the value of *str* is ignored leaving ``__str__`` to base + classes. + + .. [#] + Note that *attrs* will *not* remove existing implementations of + ``__hash__`` or the equality methods. It just won't add own + ones. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + A callable that is run whenever the user attempts to set an + attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same + arguments as validators: the instance, the attribute that is being + modified, and the new value. + + If no exception is raised, the attribute is set to the return value + of the callable. + + If a list of callables is passed, they're automatically wrapped in + an `attrs.setters.pipe`. + + If left None, the default behavior is to run converters and + validators whenever an attribute is set. + + init (bool): + Create a ``__init__`` method that initializes the *attrs* + attributes. Leading underscores are stripped for the argument name, + unless an alias is set on the attribute. + + .. seealso:: + `init` shows advanced ways to customize the generated + ``__init__`` method, including executing code before and after. + + repr(bool): + Create a ``__repr__`` method with a human readable representation + of *attrs* attributes. + + str (bool): + Create a ``__str__`` method that is identical to ``__repr__``. This + is usually not necessary except for `Exception`\ s. + + eq (bool | None): + If True or None (default), add ``__eq__`` and ``__ne__`` methods + that check two instances for equality. + + .. seealso:: + `comparison` describes how to customize the comparison behavior + going as far comparing NumPy arrays. + + order (bool | None): + If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` + methods that behave like *eq* above and allow instances to be + ordered. + + They compare the instances as if they were tuples of their *attrs* + attributes if and only if the types of both classes are + *identical*. + + If `None` mirror value of *eq*. + + .. seealso:: `comparison` + + unsafe_hash (bool | None): + If None (default), the ``__hash__`` method is generated according + how *eq* and *frozen* are set. + + 1. If *both* are True, *attrs* will generate a ``__hash__`` for + you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set + to None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning + the ``__hash__`` method of the base class will be used. If the + base class is `object`, this means it will fall back to id-based + hashing. + + Although not recommended, you can decide for yourself and force + *attrs* to create one (for example, if the class is immutable even + though you didn't freeze it programmatically) by passing True or + not. Both of these cases are rather special and should be used + carefully. + + .. seealso:: + + - Our documentation on `hashing`, + - Python's documentation on `object.__hash__`, + - and the `GitHub issue that led to the default \ behavior + `_ for more + details. + + hash (bool | None): + Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence. + + cache_hash (bool): + Ensure that the object's hash code is computed only once and stored + on the object. If this is set to True, hashing must be either + explicitly or implicitly enabled for this class. If the hash code + is cached, avoid any reassignments of fields involved in hash code + computation or mutations of the objects those fields point to after + object creation. If such changes occur, the behavior of the + object's hash code is undefined. + + frozen (bool): + Make instances immutable after initialization. If someone attempts + to modify a frozen instance, `attrs.exceptions.FrozenInstanceError` + is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` + method on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other + words: ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You + can circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + kw_only (bool): + Make attributes keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). Attributes that + explicitly set ``kw_only=False`` are not affected; base class + attributes are also not affected. + + Also see *force_kw_only*. + + weakref_slot (bool): + Make instances weak-referenceable. This has no effect unless + *slots* is True. + + field_transformer (~typing.Callable | None): + A function that is called with the original class object and all + fields right before *attrs* finalizes the class. You can use this, + for example, to automatically add converters or validators to + fields based on their types. + + .. seealso:: `transform-fields` + + match_args (bool): + If True (default), set ``__match_args__`` on the class to support + :pep:`634` (*Structural Pattern Matching*). It is a tuple of all + non-keyword-only ``__init__`` parameter names on Python 3.10 and + later. Ignored on older Python versions. + + collect_by_mro (bool): + If True, *attrs* collects attributes from base classes correctly + according to the `method resolution order + `_. If False, *attrs* + will mimic the (wrong) behavior of `dataclasses` and :pep:`681`. + + See also `issue #428 + `_. + + force_kw_only (bool): + A back-compat flag for restoring pre-25.4.0 behavior. If True and + ``kw_only=True``, all attributes are made keyword-only, including + base class attributes, and those set to ``kw_only=False`` at the + attribute level. Defaults to False. + + See also `issue #980 + `_. + + getstate_setstate (bool | None): + .. note:: + + This is usually only interesting for slotted classes and you + should probably just set *auto_detect* to True. + + If True, ``__getstate__`` and ``__setstate__`` are generated and + attached to the class. This is necessary for slotted classes to be + pickleable. If left None, it's True by default for slotted classes + and False for dict classes. + + If *auto_detect* is True, and *getstate_setstate* is left None, and + **either** ``__getstate__`` or ``__setstate__`` is detected + directly on the class (meaning: not inherited), it is set to False + (this is usually what you want). + + auto_attribs (bool | None): + If True, look at type annotations to determine which attributes to + use, like `dataclasses`. If False, it will only look for explicit + :func:`field` class attributes, like classic *attrs*. + + If left None, it will guess: + + 1. If any attributes are annotated and no unannotated + `attrs.field`\ s are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attrs.field`\ s. + + If *attrs* decides to look at type annotations, **all** fields + **must** be annotated. If *attrs* encounters a field that is set to + a :func:`field` / `attr.ib` but lacks a type annotation, an + `attrs.exceptions.UnannotatedAttributeError` is raised. Use + ``field_name: typing.Any = field(...)`` if you don't want to set a + type. + + .. warning:: + + For features that use the attribute name to create decorators + (for example, :ref:`validators `), you still *must* + assign :func:`field` / `attr.ib` to them. Otherwise Python will + either not find the name or try to use the default value to + call, for example, ``validator`` on it. + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `field()` are **ignored**. + + these (dict[str, object]): + A dictionary of name to the (private) return value of `field()` + mappings. This is useful to avoid the definition of your attributes + within the class body because you can't (for example, if you want + to add ``__repr__`` methods to Django models) or don't want to. + + If *these* is not `None`, *attrs* will *not* search the class body + for attributes and will *not* remove any attributes from it. + + The order is deduced from the order of the attributes inside + *these*. + + Arguably, this is a rather obscure feature. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionadded:: 24.3.0 + Unless already present, a ``__replace__`` method is automatically + created for `copy.replace` (Python 3.13+ only). + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 + Added *force_kw_only* to go back to the previous *kw_only* behavior. + + .. note:: + + The main differences to the classic `attr.s` are: + + - Automatically detect whether or not *auto_attribs* should be `True` + (c.f. *auto_attribs* parameter). + - Converters and validators run when attributes are set by default -- + if *frozen* is `False`. + - *slots=True* + + Usually, this has only upsides and few visible effects in everyday + programming. But it *can* lead to some surprising behaviors, so + please make sure to read :term:`slotted classes`. + + - *auto_exc=True* + - *auto_detect=True* + - *order=False* + - *force_kw_only=False* + - Some options that were only relevant on Python 2 or were kept around + for backwards-compatibility have been removed. + + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + unsafe_hash=unsafe_hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + match_args=match_args, + force_kw_only=force_kw_only, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes convert & validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = _DEFAULT_ON_SETATTR + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." + raise ValueError(msg) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new :term:`field` / :term:`attribute` on a class. + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attrs.define` (or similar)! + + Args: + default: + A value that is used if an *attrs*-generated ``__init__`` is used + and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `attrs.Factory`, its callable will + be used to construct a new value (useful for mutable data types + like lists or dicts). + + If a default is not set (or set manually to `attrs.NOTHING`), a + value *must* be supplied when instantiating; otherwise a + `TypeError` will be raised. + + .. seealso:: `defaults` + + factory (~typing.Callable): + Syntactic sugar for ``default=attr.Factory(factory)``. + + validator (~typing.Callable | list[~typing.Callable]): + Callable that is called by *attrs*-generated ``__init__`` methods + after the instance has been initialized. They receive the + initialized instance, the :func:`~attrs.Attribute`, and the passed + value. + + The return value is *not* inspected so the validator has to throw + an exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. + + The validator can also be set using decorator notation as shown + below. + + .. seealso:: :ref:`validators` + + repr (bool | ~typing.Callable): + Include this attribute in the generated ``__repr__`` method. If + True, include the attribute; if False, omit it. By default, the + built-in ``repr()`` function is used. To override how the attribute + value is formatted, pass a ``callable`` that takes a single value + and returns a string. Note that the resulting string is used as-is, + which means it will be used directly *instead* of calling + ``repr()`` (the default). + + eq (bool | ~typing.Callable): + If True (default), include this attribute in the generated + ``__eq__`` and ``__ne__`` methods that check two instances for + equality. To override how the attribute value is compared, pass a + callable that takes a single value and returns the value to be + compared. + + .. seealso:: `comparison` + + order (bool | ~typing.Callable): + If True (default), include this attributes in the generated + ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To + override how the attribute value is ordered, pass a callable that + takes a single value and returns the value to be ordered. + + .. seealso:: `comparison` + + hash (bool | None): + Include this attribute in the generated ``__hash__`` method. If + None (default), mirror *eq*'s value. This is the correct behavior + according the Python spec. Setting this value to anything else + than None is *discouraged*. + + .. seealso:: `hashing` + + init (bool): + Include this attribute in the generated ``__init__`` method. + + It is possible to set this to False and set a default value. In + that case this attributed is unconditionally initialized with the + specified default value or factory. + + .. seealso:: `init` + + converter (typing.Callable | Converter): + A callable that is called by *attrs*-generated ``__init__`` methods + to convert attribute's value to the desired format. + + If a vanilla callable is passed, it is given the passed-in value as + the only positional argument. It is possible to receive additional + arguments by wrapping the callable in a `Converter`. + + Either way, the returned value will be used as the new value of the + attribute. The value is converted before being passed to the + validator, if any. + + .. seealso:: :ref:`converters` + + metadata (dict | None): + An arbitrary mapping, to be used by third-party code. + + .. seealso:: `extending-metadata`. + + type (type): + The type of the attribute. Nowadays, the preferred method to + specify the type is using a variable annotation (see :pep:`526`). + This argument is provided for backwards-compatibility and for usage + with `make_class`. Regardless of the approach used, the type will + be stored on ``Attribute.type``. + + Please note that *attrs* doesn't do anything with this metadata by + itself. You can use it as part of your own code or for `static type + checking `. + + kw_only (bool | None): + Make this attribute keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). If None (default), + mirror the setting from `attrs.define`. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + Allows to overwrite the *on_setattr* setting from `attr.s`. If left + None, the *on_setattr* value from `attr.s` is used. Set to + `attrs.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `define()`. + + alias (str | None): + Override this attribute's parameter name in the generated + ``__init__`` method. If left None, default to ``name`` stripped + of leading underscores. See `private-attributes`. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionadded:: 22.2.0 *alias* + .. versionadded:: 23.1.0 + The *type* parameter has been re-added; mostly for `attrs.make_class`. + Please note that type checkers ignore this metadata. + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + + .. seealso:: + + `attr.ib` + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + type=type, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + alias=alias, + ) + + +def asdict(inst, *, recurse=True, filter=None, value_serializer=None): + """ + Same as `attr.asdict`, except that collections types are always retained + and dict is always used as *dict_factory*. + + .. versionadded:: 21.3.0 + """ + return _asdict( + inst=inst, + recurse=recurse, + filter=filter, + value_serializer=value_serializer, + retain_collection_types=True, + ) + + +def astuple(inst, *, recurse=True, filter=None): + """ + Same as `attr.astuple`, except that collections types are always retained + and `tuple` is always used as the *tuple_factory*. + + .. versionadded:: 21.3.0 + """ + return _astuple( + inst=inst, recurse=recurse, filter=filter, retain_collection_types=True + ) + + +def inspect(cls): + """ + Inspect the class and return its effective build parameters. + + Warning: + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + Args: + cls: The *attrs*-decorated class to inspect. + + Returns: + The effective build parameters of the class. + + Raises: + NotAnAttrsClassError: If the class is not an *attrs*-decorated class. + + .. versionadded:: 25.4.0 + """ + try: + return cls.__dict__["__attrs_props__"] + except KeyError: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) from None diff --git a/.venv/lib/python3.11/site-packages/attr/_typing_compat.pyi b/.venv/lib/python3.11/site-packages/attr/_typing_compat.pyi new file mode 100644 index 00000000..ca7b71e9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_typing_compat.pyi @@ -0,0 +1,15 @@ +from typing import Any, ClassVar, Protocol + +# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`. +MYPY = False + +if MYPY: + # A protocol to be able to statically accept an attrs class. + class AttrsInstance_(Protocol): + __attrs_attrs__: ClassVar[Any] + +else: + # For type checkers without plug-in support use an empty protocol that + # will (hopefully) be combined into a union. + class AttrsInstance_(Protocol): + pass diff --git a/.venv/lib/python3.11/site-packages/attr/_version_info.py b/.venv/lib/python3.11/site-packages/attr/_version_info.py new file mode 100644 index 00000000..27f18884 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_version_info.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: MIT + + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo: + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them + + def __hash__(self): + return hash((self.year, self.minor, self.micro, self.releaselevel)) diff --git a/.venv/lib/python3.11/site-packages/attr/_version_info.pyi b/.venv/lib/python3.11/site-packages/attr/_version_info.pyi new file mode 100644 index 00000000..45ced086 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/.venv/lib/python3.11/site-packages/attr/converters.py b/.venv/lib/python3.11/site-packages/attr/converters.py new file mode 100644 index 00000000..0a79deef --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/converters.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful converters. +""" + +import typing + +from ._compat import _AnnotationExtractor +from ._make import NOTHING, Converter, Factory, pipe + + +__all__ = [ + "default_if_none", + "optional", + "pipe", + "to_bool", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to `None`. + + Type annotations will be inferred from the wrapped converter's, if it has + any. + + Args: + converter (typing.Callable): + the converter that is used for non-`None` values. + + .. versionadded:: 17.1.0 + """ + + if isinstance(converter, Converter): + + def optional_converter(val, inst, field): + if val is None: + return None + return converter(val, inst, field) + + else: + + def optional_converter(val): + if val is None: + return None + return converter(val) + + xtr = _AnnotationExtractor(converter) + + t = xtr.get_first_param_type() + if t: + optional_converter.__annotations__["val"] = typing.Optional[t] + + rt = xtr.get_return_type() + if rt: + optional_converter.__annotations__["return"] = typing.Optional[rt] + + if isinstance(converter, Converter): + return Converter(optional_converter, takes_self=True, takes_field=True) + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace `None` values by *default* or the result + of *factory*. + + Args: + default: + Value to be used if `None` is passed. Passing an instance of + `attrs.Factory` is supported, however the ``takes_self`` option is + *not*. + + factory (typing.Callable): + A callable that takes no parameters whose result is used if `None` + is passed. + + Raises: + TypeError: If **neither** *default* or *factory* is passed. + + TypeError: If **both** *default* and *factory* are passed. + + ValueError: + If an instance of `attrs.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + msg = "Must pass either `default` or `factory`." + raise TypeError(msg) + + if default is not NOTHING and factory is not None: + msg = "Must pass either `default` or `factory` but not both." + raise TypeError(msg) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + msg = "`takes_self` is not supported by default_if_none." + raise ValueError(msg) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter + + +def to_bool(val): + """ + Convert "boolean" strings (for example, from environment variables) to real + booleans. + + Values mapping to `True`: + + - ``True`` + - ``"true"`` / ``"t"`` + - ``"yes"`` / ``"y"`` + - ``"on"`` + - ``"1"`` + - ``1`` + + Values mapping to `False`: + + - ``False`` + - ``"false"`` / ``"f"`` + - ``"no"`` / ``"n"`` + - ``"off"`` + - ``"0"`` + - ``0`` + + Raises: + ValueError: For any other value. + + .. versionadded:: 21.3.0 + """ + if isinstance(val, str): + val = val.lower() + + if val in (True, "true", "t", "yes", "y", "on", "1", 1): + return True + if val in (False, "false", "f", "no", "n", "off", "0", 0): + return False + + msg = f"Cannot convert value to bool: {val!r}" + raise ValueError(msg) diff --git a/.venv/lib/python3.11/site-packages/attr/converters.pyi b/.venv/lib/python3.11/site-packages/attr/converters.pyi new file mode 100644 index 00000000..12bd0c4f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/converters.pyi @@ -0,0 +1,19 @@ +from typing import Callable, Any, overload + +from attrs import _ConverterType, _CallableConverterType + +@overload +def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ... +@overload +def pipe(*validators: _ConverterType) -> _ConverterType: ... +@overload +def optional(converter: _CallableConverterType) -> _CallableConverterType: ... +@overload +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: Any) -> _CallableConverterType: ... +@overload +def default_if_none( + *, factory: Callable[[], Any] +) -> _CallableConverterType: ... +def to_bool(val: str | int | bool) -> bool: ... diff --git a/.venv/lib/python3.11/site-packages/attr/exceptions.py b/.venv/lib/python3.11/site-packages/attr/exceptions.py new file mode 100644 index 00000000..3b7abb81 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/exceptions.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +from typing import ClassVar + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + msg = "can't set attribute" + args: ClassVar[tuple[str]] = [msg] + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An *attrs* function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-*attrs* class has been passed into an *attrs* function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set when defining the field and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has a field without a type annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an *attrs* feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A field requiring a callable has been set with a value that is not + callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/.venv/lib/python3.11/site-packages/attr/exceptions.pyi b/.venv/lib/python3.11/site-packages/attr/exceptions.pyi new file mode 100644 index 00000000..f2680118 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/exceptions.pyi @@ -0,0 +1,17 @@ +from typing import Any + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/.venv/lib/python3.11/site-packages/attr/filters.py b/.venv/lib/python3.11/site-packages/attr/filters.py new file mode 100644 index 00000000..689b1705 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/filters.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attrs.asdict` and `attrs.astuple`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Create a filter that only allows *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to include. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return include_ + + +def exclude(*what): + """ + Create a filter that does **not** allow *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to exclude. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return exclude_ diff --git a/.venv/lib/python3.11/site-packages/attr/filters.pyi b/.venv/lib/python3.11/site-packages/attr/filters.pyi new file mode 100644 index 00000000..974abdcd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/filters.pyi @@ -0,0 +1,6 @@ +from typing import Any + +from . import Attribute, _FilterType + +def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... +def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... diff --git a/.venv/lib/python3.11/site-packages/attr/py.typed b/.venv/lib/python3.11/site-packages/attr/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/attr/setters.py b/.venv/lib/python3.11/site-packages/attr/setters.py new file mode 100644 index 00000000..78b08398 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/setters.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly used hooks for on_setattr. +""" + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + # This can be removed once we drop 3.8 and use attrs.Converter instead. + from ._make import Converter + + if not isinstance(c, Converter): + return c(new_value) + + return c(new_value, instance, attrib) + + return new_value + + +# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. +# Sphinx's autodata stopped working, so the docstring is inlined in the API +# docs. +NO_OP = object() diff --git a/.venv/lib/python3.11/site-packages/attr/setters.pyi b/.venv/lib/python3.11/site-packages/attr/setters.pyi new file mode 100644 index 00000000..73abf36e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/setters.pyi @@ -0,0 +1,20 @@ +from typing import Any, NewType, NoReturn, TypeVar + +from . import Attribute +from attrs import _OnSetAttrType + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/.venv/lib/python3.11/site-packages/attr/validators.py b/.venv/lib/python3.11/site-packages/attr/validators.py new file mode 100644 index 00000000..837e003b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/validators.py @@ -0,0 +1,748 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful validators. +""" + +import operator +import re + +from contextlib import contextmanager +from re import Pattern + +from ._config import get_run_validators, set_run_validators +from ._make import _AndValidator, and_, attrib, attrs +from .converters import default_if_none +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "disabled", + "ge", + "get_disabled", + "gt", + "in_", + "instance_of", + "is_callable", + "le", + "lt", + "matches_re", + "max_len", + "min_len", + "not_", + "optional", + "or_", + "set_disabled", +] + + +def set_disabled(disabled): + """ + Globally disable or enable running validators. + + By default, they are run. + + Args: + disabled (bool): If `True`, disable running all validators. + + .. warning:: + + This function is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(not disabled) + + +def get_disabled(): + """ + Return a bool indicating whether validators are currently disabled or not. + + Returns: + bool:`True` if validators are currently disabled. + + .. versionadded:: 21.3.0 + """ + return not get_run_validators() + + +@contextmanager +def disabled(): + """ + Context manager that disables running validators within its context. + + .. warning:: + + This context manager is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(False) + try: + yield + finally: + set_run_validators(True) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InstanceOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type]): The type to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator: + pattern = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)" + raise ValueError( + msg, + attr, + self.pattern, + value, + ) + + def __repr__(self): + return f"" + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called with a + string that doesn't match *regex*. + + Args: + regex (str, re.Pattern): + A regex string or precompiled pattern to match against + + flags (int): + Flags that will be passed to the underlying re function (default 0) + + func (typing.Callable): + Which underlying `re` function to call. Valid options are + `re.fullmatch`, `re.search`, and `re.match`; the default `None` + means `re.fullmatch`. For performance reasons, the pattern is + always precompiled using `re.compile`. + + .. versionadded:: 19.2.0 + .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. + """ + valid_funcs = (re.fullmatch, None, re.search, re.match) + if func not in valid_funcs: + msg = "'func' must be one of {}.".format( + ", ".join( + sorted((e and e.__name__) or "None" for e in set(valid_funcs)) + ) + ) + raise ValueError(msg) + + if isinstance(regex, Pattern): + if flags: + msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead" + raise TypeError(msg) + pattern = regex + else: + pattern = re.compile(regex, flags) + + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + match_func = pattern.fullmatch + + return _MatchesReValidator(pattern, match_func) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OptionalValidator: + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return f"" + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to `None` in addition to satisfying the requirements of + the sub-validator. + + Args: + validator + (typing.Callable | tuple[typing.Callable] | list[typing.Callable]): + A validator (or validators) that is used for non-`None` values. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators. + """ + if isinstance(validator, (list, tuple)): + return _OptionalValidator(_AndValidator(validator)) + + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InValidator: + options = attrib() + _original_options = attrib(hash=False) + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})" + raise ValueError( + msg, + attr, + self._original_options, + value, + ) + + def __repr__(self): + return f"" + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called with a + value that does not belong in the *options* provided. + + The check is performed using ``value in options``, so *options* has to + support that operation. + + To keep the validator hashable, dicts, lists, and sets are transparently + transformed into a `tuple`. + + Args: + options: Allowed options. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected options, and the value it got. + + .. versionadded:: 17.1.0 + .. versionchanged:: 22.1.0 + The ValueError was incomplete until now and only contained the human + readable error message. Now it contains all the information that has + been promised since 17.1.0. + .. versionchanged:: 24.1.0 + *options* that are a list, dict, or a set are now transformed into a + tuple to keep the validator hashable. + """ + repr_options = options + if isinstance(options, (list, dict, set)): + options = tuple(options) + + return _InValidator(options, repr_options) + + +@attrs(repr=False, slots=False, unsafe_hash=True) +class _IsCallableValidator: + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attrs.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute that is + not callable. + + .. versionadded:: 19.1.0 + + Raises: + attrs.exceptions.NotCallableError: + With a human readable error message containing the attribute + (`attrs.Attribute`) name, and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepIterable: + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else f" {self.iterable_validator!r}" + ) + return ( + f"" + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + Args: + member_validator: Validator(s) to apply to iterable members. + + iterable_validator: + Validator(s) to apply to iterable itself (optional). + + Raises + TypeError: if any sub-validators fail + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *member_validator* and *iterable_validator* can now be a list or tuple + of validators. + """ + if isinstance(member_validator, (list, tuple)): + member_validator = and_(*member_validator) + if isinstance(iterable_validator, (list, tuple)): + iterable_validator = and_(*iterable_validator) + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepMapping: + key_validator = attrib(validator=optional(is_callable())) + value_validator = attrib(validator=optional(is_callable())) + mapping_validator = attrib(validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + if self.key_validator is not None: + self.key_validator(inst, attr, key) + if self.value_validator is not None: + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return f"" + + +def deep_mapping( + key_validator=None, value_validator=None, mapping_validator=None +): + """ + A validator that performs deep validation of a dictionary. + + All validators are optional, but at least one of *key_validator* or + *value_validator* must be provided. + + Args: + key_validator: Validator(s) to apply to dictionary keys. + + value_validator: Validator(s) to apply to dictionary values. + + mapping_validator: + Validator(s) to apply to top-level mapping attribute. + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *key_validator* and *value_validator* are now optional, but at least one + of them must be provided. + + .. versionchanged:: 25.4.0 + *key_validator*, *value_validator*, and *mapping_validator* can now be a + list or tuple of validators. + + Raises: + TypeError: If any sub-validator fails on validation. + + ValueError: + If neither *key_validator* nor *value_validator* is provided on + instantiation. + """ + if key_validator is None and value_validator is None: + msg = ( + "At least one of key_validator or value_validator must be provided" + ) + raise ValueError(msg) + + if isinstance(key_validator, (list, tuple)): + key_validator = and_(*key_validator) + if isinstance(value_validator, (list, tuple)): + value_validator = and_(*value_validator) + if isinstance(mapping_validator, (list, tuple)): + mapping_validator = and_(*mapping_validator) + + return _DeepMapping(key_validator, value_validator, mapping_validator) + + +@attrs(repr=False, frozen=True, slots=True) +class _NumberValidator: + bound = attrib() + compare_op = attrib() + compare_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.compare_func(value, self.bound): + msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def lt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number larger or equal to *val*. + + The validator uses `operator.lt` to compare the values. + + Args: + val: Exclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<", operator.lt) + + +def le(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number greater than *val*. + + The validator uses `operator.le` to compare the values. + + Args: + val: Inclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<=", operator.le) + + +def ge(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller than *val*. + + The validator uses `operator.ge` to compare the values. + + Args: + val: Inclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">=", operator.ge) + + +def gt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller or equal to *val*. + + The validator uses `operator.gt` to compare the values. + + Args: + val: Exclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">", operator.gt) + + +@attrs(repr=False, frozen=True, slots=True) +class _MaxLengthValidator: + max_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) > self.max_length: + msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def max_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is longer than *length*. + + Args: + length (int): Maximum length of the string or iterable + + .. versionadded:: 21.3.0 + """ + return _MaxLengthValidator(length) + + +@attrs(repr=False, frozen=True, slots=True) +class _MinLengthValidator: + min_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) < self.min_length: + msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def min_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is shorter than *length*. + + Args: + length (int): Minimum length of the string or iterable + + .. versionadded:: 22.1.0 + """ + return _MinLengthValidator(length) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _SubclassOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not issubclass(value, self.type): + msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def _subclass_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `issubclass` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type, ...]): The type(s) to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _SubclassOfValidator(type) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _NotValidator: + validator = attrib() + msg = attrib( + converter=default_if_none( + "not_ validator child '{validator!r}' " + "did not raise a captured error" + ) + ) + exc_types = attrib( + validator=deep_iterable( + member_validator=_subclass_of(Exception), + iterable_validator=instance_of(tuple), + ), + ) + + def __call__(self, inst, attr, value): + try: + self.validator(inst, attr, value) + except self.exc_types: + pass # suppress error to invert validity + else: + raise ValueError( + self.msg.format( + validator=self.validator, + exc_types=self.exc_types, + ), + attr, + self.validator, + value, + self.exc_types, + ) + + def __repr__(self): + return f"" + + +def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)): + """ + A validator that wraps and logically 'inverts' the validator passed to it. + It will raise a `ValueError` if the provided validator *doesn't* raise a + `ValueError` or `TypeError` (by default), and will suppress the exception + if the provided validator *does*. + + Intended to be used with existing validators to compose logic without + needing to create inverted variants, for example, ``not_(in_(...))``. + + Args: + validator: A validator to be logically inverted. + + msg (str): + Message to raise if validator fails. Formatted with keys + ``exc_types`` and ``validator``. + + exc_types (tuple[type, ...]): + Exception type(s) to capture. Other types raised by child + validators will not be intercepted and pass through. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the validator that failed to raise an + exception, the value it got, and the expected exception types. + + .. versionadded:: 22.2.0 + """ + try: + exc_types = tuple(exc_types) + except TypeError: + exc_types = (exc_types,) + return _NotValidator(validator, msg, exc_types) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OrValidator: + validators = attrib() + + def __call__(self, inst, attr, value): + for v in self.validators: + try: + v(inst, attr, value) + except Exception: # noqa: BLE001, PERF203, S112 + continue + else: + return + + msg = f"None of {self.validators!r} satisfied for value {value!r}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def or_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators until one of them is + satisfied. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + Raises: + ValueError: + If no validator is satisfied. Raised with a human-readable error + message listing all the wrapped validators and the value that + failed all of them. + + .. versionadded:: 24.1.0 + """ + vals = [] + for v in validators: + vals.extend(v.validators if isinstance(v, _OrValidator) else [v]) + + return _OrValidator(tuple(vals)) diff --git a/.venv/lib/python3.11/site-packages/attr/validators.pyi b/.venv/lib/python3.11/site-packages/attr/validators.pyi new file mode 100644 index 00000000..36a7e800 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attr/validators.pyi @@ -0,0 +1,140 @@ +from types import UnionType +from typing import ( + Any, + AnyStr, + Callable, + Container, + ContextManager, + Iterable, + Mapping, + Match, + Pattern, + TypeVar, + overload, +) + +from attrs import _ValidatorType +from attrs import _ValidatorArgType + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_T4 = TypeVar("_T4") +_T5 = TypeVar("_T5") +_T6 = TypeVar("_T6") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +def set_disabled(run: bool) -> None: ... +def get_disabled() -> bool: ... +def disabled() -> ContextManager[None]: ... + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: tuple[type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2]], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2], type[_T3]], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ... +@overload +def instance_of(type: UnionType) -> _ValidatorType[Any]: ... +def optional( + validator: ( + _ValidatorType[_T] + | list[_ValidatorType[_T]] + | tuple[_ValidatorType[_T]] + ), +) -> _ValidatorType[_T | None]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: Pattern[AnyStr] | AnyStr, + flags: int = ..., + func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorArgType[_T], + iterable_validator: _ValidatorArgType[_I] | None = ..., +) -> _ValidatorType[_I]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K], + value_validator: _ValidatorArgType[_V] | None = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K] | None = ..., + value_validator: _ValidatorArgType[_V] = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... +def lt(val: _T) -> _ValidatorType[_T]: ... +def le(val: _T) -> _ValidatorType[_T]: ... +def ge(val: _T) -> _ValidatorType[_T]: ... +def gt(val: _T) -> _ValidatorType[_T]: ... +def max_len(length: int) -> _ValidatorType[_T]: ... +def min_len(length: int) -> _ValidatorType[_T]: ... +def not_( + validator: _ValidatorType[_T], + *, + msg: str | None = None, + exc_types: type[Exception] | Iterable[type[Exception]] = ..., +) -> _ValidatorType[_T]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], + __v6: _ValidatorType[_T6], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5 | _T6]: ... +@overload +def or_( + __v1: _ValidatorType[Any], + __v2: _ValidatorType[Any], + __v3: _ValidatorType[Any], + __v4: _ValidatorType[Any], + __v5: _ValidatorType[Any], + __v6: _ValidatorType[Any], + *validators: _ValidatorType[Any], +) -> _ValidatorType[Any]: ... diff --git a/.venv/lib/python3.11/site-packages/attributecode/__init__.py b/.venv/lib/python3.11/site-packages/attributecode/__init__.py new file mode 100644 index 00000000..74e7215d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/__init__.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +from collections import namedtuple +import logging +import os + +import saneyaml + +__version__ = '11.1.1' + +__about_spec_version__ = '4.0.0' + +__copyright__ = """ +Copyright (c) nexB Inc. All rights reserved. http://dejacode.org +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +class Error(namedtuple('Error', ['severity', 'message'])): + """ + An Error data with a severity and message. + """ + + def __new__(self, severity, message): + if message: + if isinstance(message, str): + message = self._clean_string(message) + else: + message = self._clean_string(repr(message)) + message = message.strip('"') + + return super(Error, self).__new__( + Error, severity, message) + + def __repr__(self, *args, **kwargs): + sev, msg = self._get_values() + return 'Error(%(sev)s, %(msg)s)' % locals() + + def __eq__(self, other): + return repr(self) == repr(other) + + def _get_values(self): + sev = severities[self.severity] + msg = self._clean_string(repr(self.message)) + return sev, msg + + def render(self): + sev, msg = self._get_values() + return '%(sev)s: %(msg)s' % locals() + + def to_dict(self, *args, **kwargs): + """ + Return an ordered dict of self. + """ + return self._asdict() + + @staticmethod + def _clean_string(s): + """ + Return a cleaned string for `s`, stripping eventual "u" prefixes + from unicode representations. + """ + if not s: + return s + if s.startswith(('u"', "u'")): + s = s.lstrip('u') + s = s.replace('[u"', '["') + s = s.replace("[u'", "['") + s = s.replace("(u'", "('") + s = s.replace("(u'", "('") + s = s.replace("{u'", "{'") + s = s.replace("{u'", "{'") + s = s.replace(" u'", " '") + s = s.replace(" u'", " '") + s = s.replace("\\\\", "\\") + return s + + +# modeled after the logging levels +CRITICAL = 50 +ERROR = 40 +WARNING = 30 +INFO = 20 +DEBUG = 10 +NOTSET = 0 + +severities = { + CRITICAL: 'CRITICAL', + ERROR: 'ERROR', + WARNING: 'WARNING', + INFO: 'INFO', + DEBUG: 'DEBUG', + NOTSET: 'NOTSET' +} diff --git a/.venv/lib/python3.11/site-packages/attributecode/__main__.py b/.venv/lib/python3.11/site-packages/attributecode/__main__.py new file mode 100644 index 00000000..b1d85141 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/__main__.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +if __name__ == '__main__': # pragma: nocover + from attributecode import cmd + cmd.about() diff --git a/.venv/lib/python3.11/site-packages/attributecode/api.py b/.venv/lib/python3.11/site-packages/attributecode/api.py new file mode 100644 index 00000000..4763aa59 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/api.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import json +from requests import get + +from urllib.parse import quote +from urllib.parse import urlencode +from urllib.error import HTTPError + +from attributecode import ERROR +from attributecode import Error + +""" +API call helpers +""" + + +# FIXME: args should start with license_key +def request_license_data(api_url, api_key, license_key): + """ + Return a tuple of (dictionary of license data, list of errors) given a + `license_key`. Send a request to `api_url` authenticating with `api_key`. + """ + headers = { + 'Authorization': 'Token %s' % api_key, + } + payload = { + 'api_key': api_key, + 'key': license_key, + 'format': 'json' + } + + api_url = api_url.rstrip('/') + payload = urlencode(payload) + + full_url = '%(api_url)s/?%(payload)s' % locals() + # handle special characters in URL such as space etc. + quoted_url = quote(full_url, safe="%/:=&?~#+!$,;'@()*[]") + + license_data = {} + errors = [] + try: + response = get(quoted_url, headers=headers) + response_content = response.text + # FIXME: this should be an ordered dict + license_data = json.loads(response_content) + if not license_data.get('results', []): + msg = u"Invalid 'license': %s" % license_key + errors.append(Error(ERROR, msg)) + except HTTPError as http_e: + msg = (u"Authorization denied. Invalid '--api_key'. " + u"License generation is skipped.") + errors.append(Error(ERROR, msg)) + except Exception as e: + # Already checked the authorization and accessible of the URL. + # The only exception left is URL is accessible, but it's not a valid API URL + msg = (u"Invalid '--api_url'. " + u"License generation is skipped.") + errors.append(Error(ERROR, msg)) + + finally: + if license_data.get('count') == 1: + license_data = license_data.get('results')[0] + else: + license_data = {} + + return license_data, errors + + +# FIXME: args should start with license_key +def get_license_details_from_api(api_url, api_key, license_key): + """ + Return a tuple of license data given a `license_key` using the `api_url` + authenticating with `api_key`. + The details are a tuple of (license_name, license_key, license_text, errors) + where errors is a list of strings. + Missing values are provided as empty strings. + """ + license_data, errors = request_license_data(api_url, api_key, license_key) + return license_data, errors diff --git a/.venv/lib/python3.11/site-packages/attributecode/attrib.py b/.venv/lib/python3.11/site-packages/attributecode/attrib.py new file mode 100644 index 00000000..b22c6d93 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/attrib.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import datetime +import os + +import jinja2 + +from attributecode import __version__ +from attributecode import CRITICAL +from attributecode import ERROR +from attributecode import Error +from attributecode.licenses import COMMON_LICENSES +from attributecode.model import parse_license_expression +from attributecode.model import License, StringField +from attributecode.util import add_unc +from attributecode.attrib_util import multi_sort + +DEFAULT_TEMPLATE_FILE = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'templates', 'default_html.template') + +DEFAULT_TEMPLATE_SCANCODE_FILE = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'templates', 'scancode_html.template') + +DEFAULT_LICENSE_SCORE = 100 + + +def generate(abouts, is_about_input, license_dict, scancode, min_license_score, template=None, vartext=None): + """ + Generate an attribution text from an `abouts` list of About objects, a + `template` template text and a `vartext` optional dict of extra + variables. + + Return a tuple of (error, attribution text) where error is an Error object + or None and attribution text is the generated text or None. + """ + rendered = None + errors = [] + template_error = check_template(template) + if template_error: + lineno, message = template_error + error = Error( + CRITICAL, + 'Template validation error at line: {lineno}: "{message}"'.format( + **locals()) + ) + errors.append(error) + return error, None + + template = jinja2.Template(template) + # Get the current UTC time + utcnow = datetime.datetime.utcnow() + + licenses_list = [] + lic_name_expression_list = [] + if is_about_input: + for about in abouts: + # about.license_file.value is a OrderDict with license_file_name as + # the key and the license text as the value + index = 0 + for lic_name in about.license_name.value: + if about.license_key.value: + key = about.license_key.value[index] + else: + key = lic_name + captured = False + for lic in licenses_list: + if key in lic.key: + captured = True + break + if not captured or not licenses_list: + name = lic_name + if about.license_file.value.keys(): + filename = list(about.license_file.value.keys())[index] + text = list(about.license_file.value.values())[index] + else: + error = Error( + CRITICAL, 'No license file found for ' + name) + errors.append(error) + break + if about.license_url.value: + url = about.license_url.value[index] + else: + url = '' + license_object = License(key, name, filename, url, text) + licenses_list.append(license_object) + index = index + 1 + else: + # Create license object + for key in license_dict: + name = license_dict[key][0] + filename = license_dict[key][1] + text = license_dict[key][2] + url = license_dict[key][3] + license_object = License(key, name, filename, url, text) + licenses_list.append(license_object) + + # We need special treatment for scancode input. + # Each about_object may have duplicated license key and same/different license score + # We will only keep the unique license key with the highest license score. + # The process will update the license_key, license_name and license_score. + if scancode: + abouts, meet_score_licenses_list = generate_sctk_input( + abouts, min_license_score, license_dict) + # Remove the license object + remove_list = [] + for lic in licenses_list: + if lic.key not in meet_score_licenses_list: + remove_list.append(lic) + + for lic in remove_list: + licenses_list.remove(lic) + + for about in abouts: + # Create a license expression with license name + lic_name_expression = '' + lic_name_expression_list = [] + if about.license_expression.value: + for segment in about.license_expression.value.split(): + not_lic = True + for lic in licenses_list: + if segment == lic.key: + lic_name_expression_list.append(lic.name) + not_lic = False + break + if not_lic: + lic_name_expression_list.append(segment) + # Join the license name expression into a single string + lic_name_expression = ' '.join(lic_name_expression_list) + + # Add the license name expression string into the about object as a custom field + custom_field = StringField( + name='license_name_expression', value=lic_name_expression, present=True) + setattr(about, 'license_name_expression', custom_field) + + # Sort the about objects by name + abouts = sorted(abouts, key=lambda x: x.name.value.lower()) + + # Sort the license object by key + licenses_list = sorted(licenses_list, key=lambda x: x.key) + + rendered = template.render( + abouts=abouts, + common_licenses=COMMON_LICENSES, + licenses_list=licenses_list, + utcnow=utcnow, + tkversion=__version__, + vartext=vartext + ) + + return errors, rendered + + +def generate_sctk_input(abouts, min_license_score, license_dict): + meet_score_licenses_list = [] + for about in abouts: + # We will use a dictionary to keep the unique license key + # which the dictionary key is the license key and the dictionary value + # is (lic_score, lic_name) + if about.license_key.value: + updated_dict = {} + lic_key = about.license_key.value + lic_name = [] + if about.license_name.value: + lic_name = about.license_name.value + else: + lic_name = [] + for key_list in lic_key: + lic_name_list = [] + for k in key_list: + try: + lic_name_list.append(license_dict[k][0]) + except: + lic_name_list.append(k) + lic_name.append(lic_name_list) + about.license_name.value = lic_name + + if not lic_name: + lic_name = [] + for key in lic_key: + lic_name.append(license_dict[key][0]) + lic_score = about.license_score.value + assert len(lic_key) == len(lic_name) + assert len(lic_key) == len(lic_score) + + lic_key_expression = about.license_key_expression.value + if lic_key_expression: + updated_lic_key_expression = [] + removed_index = [] + for index, key in enumerate(lic_key_expression): + if key in updated_dict: + previous_score, _name = updated_dict[key] + current_score = lic_score[index] + if current_score > previous_score: + updated_dict[key] = ( + lic_score[index], lic_name[index]) + # Track the duplicated index + removed_index.append(index) + else: + updated_dict[key] = ( + lic_score[index], lic_name[index]) + updated_lic_key_expression.append(key) + # Remove the duplication + for index, key in enumerate(about.license_key.value): + if index in removed_index: + del about.license_key.value[index] + del about.license_name.value[index] + del about.license_score.value[index] + + lic_key_expression = updated_lic_key_expression + updated_lic_key = [] + updated_lic_name = [] + updated_lic_score = [] + for index, lic in enumerate(updated_dict): + _sp_char, lic_keys, _invalid_lic_exp = parse_license_expression( + lic) + score, name = updated_dict[lic] + if score >= min_license_score: + for lic_key in lic_keys: + if not lic_key in meet_score_licenses_list: + meet_score_licenses_list.append(lic_key) + + updated_lic_key.append(lic_keys) + updated_lic_name.append(name) + updated_lic_score.append(score) + + # Remove items that don't meet to score + for index, score in enumerate(updated_lic_score): + if score < min_license_score: + del updated_lic_key[index] + del updated_lic_name[index] + del updated_lic_score[index] + del lic_key_expression[index] + + about.license_key.value = updated_lic_key + about.license_name.value = updated_lic_name + about.license_score.value = updated_lic_score + about.license_key_expression.value = lic_key_expression + return abouts, meet_score_licenses_list + + +def get_license_file_key(license_text_name): + if license_text_name.endswith('.LICENSE'): + # See https://github.com/aboutcode-org/aboutcode-toolkit/issues/439 + # for why using split instead of strip + return license_text_name.rsplit('.', 1)[0] + else: + return license_text_name + + +def check_template(template_string): + """ + Check the syntax of a template. Return an error tuple (line number, + message) if the template is invalid or None if it is valid. + """ + try: + jinja2.filters.FILTERS['multi_sort'] = multi_sort + jinja2.Template(template_string) + except (jinja2.TemplateSyntaxError, jinja2.TemplateAssertionError) as e: + return e.lineno, e.message + + +def generate_from_file(abouts, is_about_input, license_dict, scancode, min_license_score, template_loc=None, vartext=None): + """ + Generate an attribution text from an `abouts` list of About objects, a + `template_loc` template file location and a `vartext` optional + dict of extra variables. + + Return a tuple of (error, attribution text) where error is an Error object + or None and attribution text is the generated text or None. + """ + if not template_loc: + if scancode: + template_loc = add_unc(DEFAULT_TEMPLATE_SCANCODE_FILE) + else: + template_loc = add_unc(DEFAULT_TEMPLATE_FILE) + else: + template_loc = add_unc(template_loc) + with open(template_loc, encoding='utf-8', errors='replace') as tplf: + tpls = tplf.read() + return generate(abouts, is_about_input, license_dict, scancode, min_license_score, template=tpls, vartext=vartext) + + +def generate_and_save(abouts, is_about_input, license_dict, output_location, scancode=False, min_license_score=0, template_loc=None, vartext=None): + """ + Generate an attribution text from an `abouts` list of About objects, a + `template_loc` template file location and a `vartext` optional + dict of extra variables. Save the generated attribution text in the + `output_location` file. + Return a list of Error objects if any. + """ + errors = [] + # Parse license_expression and save to the license list + for about in abouts: + if not about.license_expression.value: + continue + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + about.license_expression.value) + if special_char_in_expression or invalid_lic_exp: + if special_char_in_expression: + msg = (u"The following character(s) cannot be in the license_expression: " + + str(special_char_in_expression)) + else: + msg = (u"This license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + + rendering_error, rendered = generate_from_file( + abouts, + is_about_input, + license_dict, + scancode=scancode, + min_license_score=min_license_score, + template_loc=template_loc, + vartext=vartext, + ) + + if rendering_error: + errors.append(rendering_error) + + if rendered: + output_location = add_unc(output_location) + with open(output_location, 'w', encoding='utf-8', errors='replace') as of: + of.write(rendered) + + return errors, rendered diff --git a/.venv/lib/python3.11/site-packages/attributecode/attrib_util.py b/.venv/lib/python3.11/site-packages/attributecode/attrib_util.py new file mode 100644 index 00000000..3b919abf --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/attrib_util.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +from jinja2 import Environment +try: + from jinja2.filters import pass_environment +except ImportError: + from jinja2.filters import environmentfilter as pass_environment +from jinja2.filters import make_attrgetter +from jinja2.filters import ignore_case +from jinja2.filters import FilterArgumentError + +""" +Extra JINJA2 custom filters and other template utilities. +""" + + +def get_template(template_text): + """ + Return a template built from a text string. + Register custom templates as needed. + """ + env = Environment(autoescape=True) + # register our custom filters + env.filters.update(dict( + unique_together=unique_together, + multi_sort=multi_sort)) + return env.from_string(template_text) + + +@pass_environment +def multi_sort(environment, value, reverse=False, case_sensitive=False, + attributes=None): + """ + Sort an iterable using an "attributes" list of attribute names available on + each iterable item. Sort ascending unless reverse is "true". Ignore the case + of strings unless "case_sensitive" is "true". + + .. sourcecode:: jinja + + {% for item in iterable|multi_sort(attributes=['date', 'name']) %} + ... + {% endfor %} + """ + if not attributes: + raise FilterArgumentError( + 'The multi_sort filter requires a list of attributes as argument, ' + 'such as in: ' + "for item in iterable|multi_sort(attributes=['date', 'name'])") + + # build a list of attribute getters, one for each attribute + do_ignore_case = ignore_case if not case_sensitive else None + attribute_getters = [] + for attribute in attributes: + ag = make_attrgetter(environment, attribute, postprocess=do_ignore_case) + attribute_getters.append(ag) + + # build a key function that has runs all attribute getters + def key(v): + return [a(v) for a in attribute_getters] + + return sorted(value, key=key, reverse=reverse) + + +@pass_environment +def unique_together(environment, value, case_sensitive=False, attributes=None): + """ + Return a list of unique items from an iterable. Unicity is checked when + considering together all the values of an "attributes" list of attribute + names available on each iterable item.. The items order is preserved. Ignore + the case of strings unless "case_sensitive" is "true". + .. sourcecode:: jinja + + {% for item in iterable|unique_together(attributes=['date', 'name']) %} + ... + {% endfor %} + + """ + if not attributes: + raise FilterArgumentError( + 'The unique_together filter requires a list of attributes as argument, ' + 'such as in: ' + "{% for item in iterable|unique_together(attributes=['date', 'name']) %} ") + + # build a list of attribute getters, one for each attribute + do_ignore_case = ignore_case if not case_sensitive else None + attribute_getters = [] + for attribute in attributes: + ag = make_attrgetter(environment, attribute, postprocess=do_ignore_case) + attribute_getters.append(ag) + + # build a unique_key function that has runs all attribute getters + # and returns a hashable tuple + def unique_key(v): + return tuple(repr(a(v)) for a in attribute_getters) + + unique = [] + seen = set() + for item in value: + key = unique_key(item) + if key not in seen: + seen.add(key) + unique.append(item) + return unique diff --git a/.venv/lib/python3.11/site-packages/attributecode/cmd.py b/.venv/lib/python3.11/site-packages/attributecode/cmd.py new file mode 100644 index 00000000..5f2f5047 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/cmd.py @@ -0,0 +1,966 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +from attributecode.util import write_licenses +from attributecode.util import get_temp_dir +from attributecode.util import filter_errors +from attributecode.util import extract_zip +from attributecode.transform import Transformer +from attributecode.transform import write_excel +from attributecode.transform import write_json +from attributecode.transform import write_csv +from attributecode.transform import transform_excel +from attributecode.transform import transform_json +from attributecode.transform import transform_csv +from attributecode.transform import transform_data +from attributecode.model import write_output +from attributecode.model import pre_process_and_fetch_license_dict +from attributecode.model import get_copy_list +from attributecode.model import copy_redist_src +from attributecode.model import collect_inventory, collect_abouts_license_expression, collect_inventory_license_expression +from attributecode.gen import generate as generate_about_files, load_inventory +from attributecode.attrib import generate_and_save as generate_attribution_doc +from attributecode.attrib import DEFAULT_LICENSE_SCORE +from attributecode.attrib import check_template +from attributecode import severities +from attributecode import __version__ +from attributecode import __about_spec_version__ +from attributecode.util import unique +from attributecode import WARNING + +from collections import defaultdict +from functools import partial + +import os +import sys + +import click + +# silence unicode literals warnings +click.disable_unicode_literals_warning = True + + +__copyright__ = """ + Copyright (c) nexB Inc and others. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.""" + +prog_name = 'AboutCode-toolkit' + +intro = '''%(prog_name)s version %(__version__)s +ABOUT spec version: %(__about_spec_version__)s +https://aboutcode.org +%(__copyright__)s +''' % locals() + + +def print_version(): + click.echo('Running aboutcode-toolkit version ' + __version__) + + +class AboutCommand(click.Command): + """ + An enhanced click Command working around some Click quirk. + """ + + def main(self, args=None, prog_name=None, complete_var=None, + standalone_mode=True, **extra): + """ + Workaround click bug https://github.com/mitsuhiko/click/issues/365 + """ + return click.Command.main( + self, args=args, prog_name=self.name, + complete_var=complete_var, standalone_mode=standalone_mode, **extra) + + +# we define a main entry command with subcommands +@click.group(name='about') +@click.version_option(version=__version__, prog_name=prog_name, message=intro) +@click.help_option('-h', '--help') +def about(): + """ +Generate licensing attribution and credit notices from .ABOUT files and inventories. + +Read, write and collect provenance and license inventories from .ABOUT files to and from JSON or CSV files. + +Use about --help for help on a command. + """ + +###################################################################### +# option validators +###################################################################### + + +def validate_key_values(ctx, param, value): + """ + Return the a dict of {key: value} if valid or raise a UsageError + otherwise. + """ + if not value: + return + + kvals, errors = parse_key_values(value) + if errors: + ive = '\n'.join(sorted(' ' + x for x in errors)) + msg = ('Invalid {param} option(s):\n' + '{ive}'.format(**locals())) + raise click.UsageError(msg) + return kvals + + +def validate_extensions(ctx, param, value, extensions=tuple(('.csv', '.json',))): + if not value: + return + if not value.endswith(extensions): + msg = ' '.join(extensions) + raise click.UsageError( + 'Invalid {param} file extension: must be one of: {msg}'.format(**locals())) + return value + +###################################################################### +# inventory subcommand +###################################################################### + + +@about.command(cls=AboutCommand, + short_help='Collect the inventory of .ABOUT files to a CSV/JSON/XLSX file or stdout.') +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + metavar='OUTPUT') +@click.option('--exclude', + multiple=True, + metavar='PATTERN', + help='Exclude the processing of the specified input pattern (e.g. *tests* or test/).') +@click.option('-f', '--format', + is_flag=False, + default='csv', + show_default=True, + type=click.Choice(['json', 'csv', 'excel']), + help='Set OUTPUT inventory file format.') +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def inventory(location, output, exclude, format, quiet, verbose): # NOQA + """ +Collect the inventory of .ABOUT files to a CSV/JSON/XLSX file. + +LOCATION: Path to an ABOUT file or a directory with ABOUT files. + +OUTPUT: Path to the CSV/JSON/XLSX inventory file to create, or +using '-' to print result on screen/to stdout (Excel-formatted output +cannot be used in stdout). + """ + # We are not using type=click.Path() to validate the output location as + # it does not support `-` , which is used to print the result to stdout. + if not output == '-': + parent_dir = os.path.dirname(output) + if not os.path.exists(parent_dir): + msg = 'The OUTPUT directory: {parent_dir} does not exist.'.format(**locals()) + msg += '\nPlease correct and re-run' + click.echo(msg) + sys.exit(1) + else: + # Check the format if output is stdout as xlsx format cannot be displayed. + if format == 'excel': + msg = 'Excel-formatted output cannot be used in stdout.' + click.echo(msg) + sys.exit(0) + if not quiet: + print_version() + click.echo('Collecting inventory from ABOUT files...') + + if location.lower().endswith('.zip'): + # accept zipped ABOUT files as input + location = extract_zip(location) + errors, abouts = collect_inventory(location, exclude) + write_output(abouts=abouts, location=output, format=format) + + if output == '-': + log_file_loc = None + else: + log_file_loc = output + '-error.log' + errors_count = report_errors( + errors, quiet, verbose, log_file_loc) + if not quiet and not output == '-': + msg = 'Inventory collected in {output}.'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) + +###################################################################### +# gen subcommand +###################################################################### + + +@about.command(cls=AboutCommand, + short_help='Generate .ABOUT files from an inventory as CSV/JSON/XLSX.') +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=True, file_okay=False, writable=True, resolve_path=True)) +@click.option('--android', + is_flag=True, + help='Generate MODULE_LICENSE_XXX (XXX will be replaced by license key) and NOTICE ' + 'as the same design as from Android.') +# FIXME: the CLI UX should be improved with two separate options for API key and URL +@click.option('--fetch-license', + is_flag=True, + help='Fetch license data and text files from the ScanCode LicenseDB.') +@click.option('--fetch-license-djc', + nargs=2, + type=str, + metavar='api_url api_key', + help='Fetch license data and text files from a DejaCode License Library ' + 'API URL using the API KEY.') +@click.option('--scancode', + is_flag=True, + help='Indicate the input JSON file is from scancode_toolkit.') +@click.option('--reference', + metavar='DIR', + type=click.Path(exists=True, file_okay=False, + readable=True, resolve_path=True), + help='Path to a directory with reference license data and text files.') +@click.option('--worksheet', + metavar='name', + help='The worksheet name from the INPUT. (Default: the "active" worksheet)') +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def gen(location, output, android, fetch_license, fetch_license_djc, scancode, reference, worksheet, quiet, verbose): + """ +Given a CSV/JSON/XLSX inventory, generate ABOUT files in the output location. + +LOCATION: Path to a JSON/CSV/XLSX inventory file. + +OUTPUT: Path to a directory where ABOUT files are generated. + """ + if not quiet: + print_version() + click.echo('Generating .ABOUT files...') + + # FIXME: This should be checked in the `click` + if not location.endswith(('.csv', '.json', '.xlsx')): + raise click.UsageError( + 'ERROR: Invalid input file extension: must be one .csv or .json or .xlsx.') + + if worksheet and not location.endswith('.xlsx'): + raise click.UsageError( + 'ERROR: --worksheet option only works with .xlsx input.') + + errors, abouts = generate_about_files( + location=location, + base_dir=output, + android=android, + reference_dir=reference, + fetch_license=fetch_license, + fetch_license_djc=fetch_license_djc, + scancode=scancode, + worksheet=worksheet + ) + + errors_count = report_errors( + errors, quiet, verbose, log_file_loc=output + '-error.log') + if not quiet: + abouts_count = len(abouts) + msg = '{abouts_count} .ABOUT files generated in {output}.'.format( + **locals()) + click.echo(msg) + sys.exit(errors_count) + + +###################################################################### +# gen_license subcommand +###################################################################### + +@about.command(cls=AboutCommand, + short_help='Fetch and save all the licenses in the license_expression field to a directory.') +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=True, file_okay=False, writable=True, resolve_path=True)) +@click.option('--djc', + nargs=2, + type=str, + metavar='api_url api_key', + help='Fetch licenses from a DejaCode License Library.') +@click.option('--scancode', + is_flag=True, + help='Indicate the input JSON file is from scancode_toolkit.') +@click.option('--worksheet', + metavar='name', + help='The worksheet name from the INPUT. (Default: the "active" worksheet)') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def gen_license(location, output, djc, scancode, worksheet, verbose): + """ +Fetch licenses (Default: ScanCode LicenseDB) in the license_expression field and save to the output location. + +LOCATION: Path to a JSON/CSV/XLSX/.ABOUT file(s) + +OUTPUT: Path to a directory where license files are saved. + """ + print_version() + api_url = '' + api_key = '' + errors = [] + + if worksheet and not location.endswith('.xlsx'): + raise click.UsageError( + 'ERROR: --worksheet option only works with .xlsx input.') + + log_file_loc = os.path.join(output, 'error.log') + + if location.endswith('.csv') or location.endswith('.json') or location.endswith('.xlsx'): + errors, abouts = collect_inventory_license_expression( + location=location, scancode=scancode, worksheet=worksheet) + if errors: + severe_errors_count = report_errors( + errors, quiet=False, verbose=verbose, log_file_loc=log_file_loc) + sys.exit(severe_errors_count) + else: + # _errors, abouts = collect_inventory(location) + errors, abouts = collect_abouts_license_expression(location) + + if djc: + # Strip the ' and " for api_url, and api_key from input + api_url = djc[0].strip("'").strip('"') + api_key = djc[1].strip("'").strip('"') + + click.echo('Fetching licenses...') + from_check = False + license_dict, lic_errors = pre_process_and_fetch_license_dict( + abouts, from_check, api_url, api_key, scancode) + + if lic_errors: + errors.extend(lic_errors) + + # A dictionary with license file name as the key and context as the value + lic_dict_output = {} + for key in license_dict: + if not key in lic_dict_output: + lic_filename = license_dict[key][1] + lic_context = license_dict[key][2] + lic_dict_output[lic_filename] = lic_context + + write_errors = write_licenses(lic_dict_output, output) + if write_errors: + errors.extend(write_errors) + + severe_errors_count = report_errors( + errors, quiet=False, verbose=verbose, log_file_loc=log_file_loc) + sys.exit(severe_errors_count) + + +###################################################################### +# attrib subcommand +###################################################################### + + +def validate_template(ctx, param, value): + if not value: + return None + + with open(value, encoding='utf-8', errors='replace') as templatef: + template_error = check_template(templatef.read()) + + if template_error: + lineno, message = template_error + raise click.UsageError( + 'Template syntax error at line: ' + '{lineno}: "{message}"'.format(**locals())) + return value + + +@about.command(cls=AboutCommand, + short_help='Generate an attribution document from JSON/CSV/XLSX/.ABOUT files.') +@click.argument('input', + required=True, + metavar='INPUT', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=False, dir_okay=False, writable=True, resolve_path=True)) +@click.option('--api_url', + nargs=1, + type=click.STRING, + metavar='URL', + help='URL to DejaCode License Library.') +@click.option('--api_key', + nargs=1, + type=click.STRING, + metavar='KEY', + help='API Key for the DejaCode License Library') +@click.option('--min-license-score', + type=int, + help='Attribute components that have license score higher than or equal to the defined ' + '--min-license-score.') +@click.option('--scancode', + is_flag=True, + help='Indicate the input JSON file is from scancode_toolkit.') +@click.option('--reference', + metavar='DIR', + type=click.Path(exists=True, file_okay=False, + readable=True, resolve_path=True), + help='Path to a directory with reference files where "license_file" and/or "notice_file"' + ' located.') +@click.option('--template', + metavar='FILE', + callback=validate_template, + type=click.Path(exists=True, dir_okay=False, + readable=True, resolve_path=True), + help='Path to an optional custom attribution template to generate the ' + 'attribution document. If not provided the default built-in template is used.') +@click.option('--vartext', + multiple=True, + callback=validate_key_values, + metavar='=', + help='Add variable text as key=value for use in a custom attribution template.') +@click.option('--worksheet', + metavar='name', + help='The worksheet name from the INPUT. (Default: the "active" worksheet)') +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def attrib(input, output, api_url, api_key, scancode, min_license_score, reference, template, vartext, worksheet, quiet, verbose): + """ +Generate an attribution document at OUTPUT using JSON, CSV or XLSX or .ABOUT files at INPUT. + +INPUT: Path to a file (.ABOUT/.csv/.json/.xlsx), directory or .zip archive containing .ABOUT files. + +OUTPUT: Path where to write the attribution document. + """ + # A variable to define if the input ABOUT file(s) + is_about_input = False + + rendered = '' + license_dict = {} + errors = [] + + if worksheet and not input.endswith('.xlsx'): + raise click.UsageError( + 'ERROR: --worksheet option only works with .xlsx input.') + + if not quiet: + print_version() + click.echo('Generating attribution...') + + # accept zipped ABOUT files as input + if input.lower().endswith('.zip'): + input = extract_zip(input) + + if scancode: + if not input.endswith('.json'): + msg = 'The input file from scancode toolkit needs to be in JSON format.' + click.echo(msg) + sys.exit(1) + if not min_license_score and not min_license_score == 0: + min_license_score = DEFAULT_LICENSE_SCORE + + if min_license_score: + if not scancode: + msg = ('This option requires a JSON file generated by scancode toolkit as the input. ' + + 'The "--scancode" option is required.') + click.echo(msg) + sys.exit(1) + + if input.endswith('.json') or input.endswith('.csv') or input.endswith('.xlsx'): + is_about_input = False + from_attrib = True + if not reference: + # Set current directory as the reference dir + reference = os.path.dirname(input) + # Since the errors from load_inventory is only about field formatting or + # empty field which is irrelevant for attribtion process, + # See https://github.com/nexB/aboutcode-toolkit/issues/524 + # I believe we do not need to capture these errors in attrib process + errors, abouts = load_inventory( + location=input, + from_attrib=from_attrib, + scancode=scancode, + reference_dir=reference, + worksheet=worksheet + ) + + # Exit if CRITICAL error + if errors: + for e in errors: + if severities[e.severity] == 'CRITICAL': + click.echo(e) + sys.exit(1) + + else: + is_about_input = True + _errors, abouts = collect_inventory(input) + + if not abouts: + msg = 'No ABOUT file or reference is found from the input. Attribution generation halted.' + click.echo(msg) + errors_count = 1 + sys.exit(errors_count) + + if not is_about_input: + # Check if both api_url and api_key present + if api_url or api_key: + if not api_url: + msg = '"--api_url" is required.' + click.echo(msg) + sys.exit(1) + if not api_key: + msg = '"--api_key" is required.' + click.echo(msg) + sys.exit(1) + else: + api_url = '' + api_key = '' + api_url = api_url.strip("'").strip('"') + api_key = api_key.strip("'").strip('"') + from_check = False + license_dict, lic_errors = pre_process_and_fetch_license_dict( + abouts, from_check, api_url, api_key, scancode, reference) + errors.extend(lic_errors) + sorted_license_dict = sorted(license_dict) + + # Read the license_file and store in a dictionary + for about in abouts: + if about.license_file.value or about.notice_file.value: + if not reference: + msg = ( + '"license_file" / "notice_file" field contains value. Use `--reference` to indicate its parent directory.') + click.echo(msg) + # sys.exit(1) + + if abouts: + attrib_errors, rendered = generate_attribution_doc( + abouts=abouts, + is_about_input=is_about_input, + license_dict=dict(sorted(license_dict.items())), + output_location=output, + scancode=scancode, + min_license_score=min_license_score, + template_loc=template, + vartext=vartext, + ) + errors.extend(attrib_errors) + + errors_count = report_errors( + errors, quiet, verbose, log_file_loc=output + '-error.log') + + if not quiet: + if rendered: + msg = 'Attribution generated in: {output}'.format(**locals()) + click.echo(msg) + else: + msg = 'Attribution generation failed.' + click.echo(msg) + sys.exit(errors_count) + +###################################################################### +# collect_redist_src subcommand +###################################################################### + + +@about.command(cls=AboutCommand, + short_help='Collect redistributable sources.') +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + metavar='OUTPUT') +@click.option('--from-inventory', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, + readable=True, resolve_path=True), + help='Path to an inventory CSV/JSON/XLSX file as the base list for files/directories ' + 'that need to be copied which have the \'redistribute\' flagged.') +@click.option('--with-structures', + is_flag=True, + help='Copy sources with directory structure.') +@click.option('--zip', + is_flag=True, + help='Zip the copied sources to the output location.') +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def collect_redist_src(location, output, from_inventory, with_structures, zip, quiet, verbose): + """ +Collect sources that have 'redistribute' flagged as 'True' in .ABOUT files or inventory +to the output location. + +LOCATION: Path to a directory containing sources that need to be copied +(and containing ABOUT files if `inventory` is not provided) + +OUTPUT: Path to a directory or a zip file where sources will be copied to. + """ + if zip: + if not output.endswith('.zip'): + click.echo('The output needs to be a zip file.') + sys.exit() + + if not quiet: + print_version() + click.echo('Collecting inventory from ABOUT files...') + + if location.lower().endswith('.zip'): + # accept zipped ABOUT files as input + location = extract_zip(location) + + if from_inventory: + errors, abouts = load_inventory(from_inventory, location) + else: + errors, abouts = collect_inventory(location) + + if zip: + # Copy to a temp location and the zip to the output location + output_location = get_temp_dir() + else: + output_location = output + + copy_list, copy_list_errors = get_copy_list(abouts, location) + copy_errors = copy_redist_src( + copy_list, location, output_location, with_structures) + + if zip: + import shutil + # Stripped the .zip extension as the `shutil.make_archive` will + # append the .zip extension + output_no_extension = output.rsplit('.', 1)[0] + shutil.make_archive(output_no_extension, 'zip', output_location) + + errors.extend(copy_list_errors) + errors.extend(copy_errors) + errors_count = report_errors( + errors, quiet, verbose, log_file_loc=output + '-error.log') + if not quiet: + msg = 'Redistributed sources are copied to {output}.'.format( + **locals()) + click.echo(msg) + sys.exit(errors_count) + +###################################################################### +# check subcommand +###################################################################### + +# FIXME: This is really only a dupe of the Inventory command + +@about.command(cls=AboutCommand, + short_help='Validate that the format of .ABOUT files is correct and report ' + 'errors and warnings.') +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) +@click.option('--exclude', + multiple=True, + metavar='PATTERN', + help='Exclude the processing of the specified input pattern (e.g. *tests* or test/).') +@click.option('--license', + is_flag=True, + help='Validate the license_expression value in the input.') +@click.option('--djc', + nargs=2, + type=str, + metavar='api_url api_key', + help='Validate license_expression from a DejaCode License Library ' + 'API URL using the API KEY.') +@click.option('--log', + nargs=1, + metavar='FILE', + help='Path to a file to save the error messages if any.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def check(location, exclude, license, djc, log, verbose): + """ +Check .ABOUT file(s) at LOCATION for validity and print error messages. + +LOCATION: Path to an ABOUT file or a directory with ABOUT files. + """ + print_version() + + if log: + # Check if the error log location exist and create the parent directory if not + parent = os.path.dirname(log) + if not parent: + os.makedirs(parent) + + api_url = '' + api_key = '' + if djc: + # Strip the ' and " for api_url, and api_key from input + api_url = djc[0].strip("'").strip('"') + api_key = djc[1].strip("'").strip('"') + click.echo('Checking ABOUT files...') + + errors, abouts = collect_inventory(location, exclude) + + # Validate license_expression + if license: + from_check = True + _key_text_dict, errs = pre_process_and_fetch_license_dict( + abouts, from_check, api_url, api_key) + for e in errs: + errors.append(e) + + severe_errors_count = report_errors( + errors, quiet=False, verbose=verbose, log_file_loc=log) + sys.exit(severe_errors_count) + +###################################################################### +# transform subcommand +###################################################################### + + +def print_config_help(ctx, param, value): + if not value or ctx.resilient_parsing: + return + from attributecode.transform import tranformer_config_help + click.echo(tranformer_config_help) + ctx.exit() + + +@about.command(cls=AboutCommand, + short_help='Transform a CSV/JSON/XLSX by applying renamings, filters and checks.') +@click.argument('location', + required=True, + callback=partial(validate_extensions, extensions=( + '.csv', '.json', '.xlsx',)), + metavar='LOCATION', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True)) +@click.argument('output', + required=True, + callback=partial(validate_extensions, extensions=( + '.csv', '.json', '.xlsx',)), + metavar='OUTPUT', + type=click.Path(exists=False, dir_okay=False, writable=True, resolve_path=True)) +@click.option('-c', '--configuration', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, + readable=True, resolve_path=True), + help='Path to an optional YAML configuration file. See --help-format for ' + 'format help.') +@click.option('--worksheet', + metavar='name', + help='The worksheet name from the INPUT. (Default: the "active" worksheet)') +@click.option('--help-format', + is_flag=True, is_eager=True, expose_value=False, + callback=print_config_help, + help='Show configuration file format help and exit.') +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') +@click.help_option('-h', '--help') +def transform(location, output, configuration, worksheet, quiet, verbose): # NOQA + """ +Transform the CSV/JSON/XLSX file at LOCATION by applying renamings, filters and checks +and then write a new CSV/JSON/XLSX to OUTPUT. + +LOCATION: Path to a CSV/JSON/XLSX file. + +OUTPUT: Path to CSV/JSON/XLSX inventory file to create. + """ + if worksheet and not location.endswith('.xlsx'): + raise click.UsageError( + 'ERROR: --worksheet option only works with .xlsx input.') + + if not configuration: + transformer = Transformer.default() + else: + transformer = Transformer.from_file(configuration) + + if not transformer: + msg = 'Cannot transform without Transformer' + click.echo(msg) + sys.exit(1) + + errors = [] + updated_data = [] + new_data = [] + + if location.endswith('.csv'): + new_data, errors = transform_csv(location) + elif location.endswith('.json'): + new_data, errors = transform_json(location) + elif location.endswith('.xlsx'): + new_data, errors = transform_excel(location, worksheet) + + if not errors: + updated_data, errors = transform_data(new_data, transformer) + + if not updated_data: + msg = 'The input is empty. Nothing is transformed.' + click.echo(msg) + sys.exit(0) + + if not errors: + if output.endswith('.csv'): + write_csv(output, updated_data) + elif output.endswith('.json'): + write_json(output, updated_data) + else: + write_excel(output, updated_data) + + if not quiet: + print_version() + click.echo('Transforming...') + + errors_count = report_errors( + errors, quiet, verbose, log_file_loc=output + '-error.log') + if not quiet and not errors: + msg = 'Transformed file is written to {output}.'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) + +###################################################################### +# Error management +###################################################################### + + +def report_errors(errors, quiet, verbose, log_file_loc=None): + """ + Report the `errors` list of Error objects to screen based on the `quiet` and + `verbose` flags. + + If `log_file_loc` file location is provided also write a verbose log to this + file. + Return True if there were severe error reported. + """ + severe_errors_count = 0 + if errors: + log_msgs, severe_errors_count = get_error_messages(errors, verbose) + if not quiet: + for msg in log_msgs: + click.echo(msg) + if log_msgs and log_file_loc: + with open(log_file_loc, 'w', encoding='utf-8', errors='replace') as lf: + lf.write('\n'.join(log_msgs)) + click.echo("Error log: " + log_file_loc) + return severe_errors_count + + +def get_error_messages(errors, verbose=False): + """ + Return a tuple of (list of error message strings to report, + severe_errors_count) given an `errors` list of Error objects and using the + `verbose` flags. + """ + if verbose: + severe_errors = errors + else: + severe_errors = filter_errors(errors, WARNING) + + severe_errors = unique(severe_errors) + severe_errors_count = len(severe_errors) + + messages = [] + + if severe_errors: + error_msg = 'Command completed with {} errors or warnings.'.format( + severe_errors_count) + messages.append(error_msg) + + for severity, message in severe_errors: + sevcode = severities.get(severity) or 'UNKNOWN' + msg = '{sevcode}: {message}'.format(**locals()) + messages.append(msg) + + return messages, severe_errors_count + +###################################################################### +# Misc +###################################################################### + + +def parse_key_values(key_values): + """ + Given a list of "key=value" strings, return: + - a dict {key: value} + - a sorted list of unique error messages for invalid entries where there is + a missing a key or value. + """ + if not key_values: + return {}, [] + + errors = set() + parsed_key_values = defaultdict(list) + for key_value in key_values: + key, _, value = key_value.partition('=') + + key = key.strip().lower() + if not key: + errors.add('missing in "{key_value}".'.format(**locals())) + continue + + value = value.strip() + if not value: + errors.add('missing in "{key_value}".'.format(**locals())) + continue + + parsed_key_values[key] = value + + return dict(parsed_key_values), sorted(errors) + + +if __name__ == '__main__': + about() diff --git a/.venv/lib/python3.11/site-packages/attributecode/gen.py b/.venv/lib/python3.11/site-packages/attributecode/gen.py new file mode 100644 index 00000000..3b824c20 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/gen.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +from posixpath import basename +from posixpath import dirname +from posixpath import exists +from posixpath import join +from posixpath import normpath + +from attributecode import ERROR +from attributecode import CRITICAL +from attributecode import INFO +from attributecode import Error +from attributecode import model +from attributecode import util +from attributecode.util import add_unc +from attributecode.util import csv +from attributecode.util import file_fields +from attributecode.util import invalid_chars +from attributecode.util import to_posix +from attributecode.util import UNC_PREFIX_POSIX +from attributecode.util import load_scancode_json, load_csv, load_json, load_excel +from attributecode.util import strip_inventory_value + + +def check_duplicated_columns(location): + """ + Return a list of errors for duplicated column names in a CSV file + at location. + """ + location = add_unc(location) + with open(location, mode='r', encoding='utf-8-sig', errors='replace') as csvfile: + reader = csv.reader(csvfile) + columns = next(reader) + columns = [col for col in columns] + + seen = set() + dupes = dict() + for col in columns: + c = col.lower() + if c in seen: + if c in dupes: + dupes[c].append(col) + else: + dupes[c] = [col] + seen.add(c.lower()) + + errors = [] + if dupes: + dup_msg = [] + for name, names in dupes.items(): + names = u', '.join(names) + msg = '%(name)s with %(names)s' % locals() + dup_msg.append(msg) + dup_msg = u', '.join(dup_msg) + msg = ('Duplicated column name(s): %(dup_msg)s\n' % locals() + + 'Please correct the input and re-run.') + err = Error(ERROR, msg) + if not err in errors: + errors.append(err) + return errors + + +def check_duplicated_about_resource(arp, arp_list): + """ + Return error for duplicated about_resource. + """ + if arp in arp_list: + msg = ("The input has duplicated values in 'about_resource' " + "field: " + arp) + return Error(CRITICAL, msg) + return '' + + +def check_newline_in_file_field(component): + """ + Return a list of errors for newline characters detected in *_file fields. + """ + errors = [] + for k in component.keys(): + if k in file_fields: + try: + if '\n' in component[k]: + if k == u'about_resource': + msg = ( + "Multiple lines detected in 'about_resource' for '%s' which is not supported.") % component['about_resource'] + else: + msg = ("New line character detected in '%s' for '%s' which is not supported." + "\nPlease use ',' to declare multiple files.") % (k, component['about_resource']) + errors.append(Error(CRITICAL, msg)) + except: + pass + return errors + + +def check_about_resource_filename(arp): + """ + Return error for invalid/non-support about_resource's filename or + empty string if no error is found. + """ + if invalid_chars(arp): + msg = ("Invalid characters present in 'about_resource' " + "field: " + arp) + return (Error(ERROR, msg)) + return '' + + +def load_inventory(location, from_attrib=False, base_dir=None, scancode=False, reference_dir=None, worksheet=None): + """ + Load the inventory file at `location` for ABOUT and LICENSE files stored in + the `base_dir`. Return a list of errors and a list of About objects + validated against the `base_dir`. + + Optionally use `reference_dir` as the directory location of extra reference + license and notice files to reuse. + """ + errors = [] + abouts = [] + is_spreadsheet = False + + if base_dir: + base_dir = util.to_posix(base_dir) + if scancode: + inventory = load_scancode_json(location) + else: + if location.endswith('.csv'): + dup_cols_err = check_duplicated_columns(location) + if dup_cols_err: + errors.extend(dup_cols_err) + return errors, abouts + inventory = load_csv(location) + is_spreadsheet = True + elif location.endswith('.xlsx'): + dup_cols_err, inventory = load_excel(location, worksheet) + is_spreadsheet = True + if dup_cols_err: + errors.extend(dup_cols_err) + return errors, abouts + else: + inventory = load_json(location) + + arp_list = [] + errors = [] + + if is_spreadsheet: + # Only the .csv and .xlsx may have newline issue + stripped_inv = strip_inventory_value(inventory) + else: + stripped_inv = inventory + + for component in stripped_inv: + if not from_attrib: + if 'about_resource' in component: + arp = component['about_resource'] + dup_err = check_duplicated_about_resource(arp, arp_list) + if dup_err: + if not dup_err in errors: + errors.append(dup_err) + else: + arp_list.append(arp) + + invalid_about_filename = check_about_resource_filename(arp) + if invalid_about_filename and not invalid_about_filename in errors: + errors.append(invalid_about_filename) + + newline_in_file_err = check_newline_in_file_field(component) + if newline_in_file_err: + errors.extend(newline_in_file_err) + + if errors: + return errors, abouts + + custom_fields_list = [] + for fields in stripped_inv: + # check does the input contains the required fields + required_fields = model.About.required_fields + + for f in required_fields: + if f not in fields: + if from_attrib and f == 'about_resource': + continue + else: + msg = "Required field: %(f)r not found in the " % locals( + ) + errors.append(Error(CRITICAL, msg)) + return errors, abouts + # Set about file path to '' if no 'about_resource' is provided from + # the input + if 'about_resource' not in fields: + afp = '' + else: + afp = fields.get(model.About.ABOUT_RESOURCE_ATTR) + + afp = util.to_posix(afp) + if base_dir: + loc = join(base_dir, afp) + else: + loc = afp + about = model.About(about_file_path=afp) + about.location = loc + + # Update value for 'about_resource' + # keep only the filename or '.' if it's a directory + if 'about_resource' in fields: + updated_resource_value = u'' + resource_path = fields['about_resource'] + if resource_path.endswith(u'/'): + updated_resource_value = u'.' + else: + updated_resource_value = basename(resource_path) + fields['about_resource'] = updated_resource_value + + ld_errors = about.load_dict( + fields, + base_dir, + scancode=scancode, + from_attrib=from_attrib, + running_inventory=False, + reference_dir=reference_dir, + ) + + for severity, message in ld_errors: + if 'Custom Field' in message: + field_name = message.replace('Custom Field: ', '').strip() + if not field_name in custom_fields_list: + custom_fields_list.append(field_name) + else: + errors.append(Error(severity, message)) + + abouts.append(about) + if custom_fields_list: + custom_fields_err_msg = 'Field ' + \ + str(custom_fields_list) + ' is a custom field.' + errors.append(Error(INFO, custom_fields_err_msg)) + + return errors, abouts + + +def update_about_resource(self): + pass + + +def generate(location, base_dir, android=None, reference_dir=None, fetch_license=False, fetch_license_djc=False, scancode=False, worksheet=None): + """ + Load ABOUT data from a CSV inventory at `location`. Write ABOUT files to + base_dir. Return errors and about objects. + """ + notice_dict = {} + api_url = '' + api_key = '' + gen_license = False + # FIXME: use two different arguments: key and url + # Check if the fetch_license contains valid argument + if fetch_license_djc: + # Strip the ' and " for api_url, and api_key from input + api_url = fetch_license_djc[0].strip("'").strip('"') + api_key = fetch_license_djc[1].strip("'").strip('"') + gen_license = True + + if fetch_license: + gen_license = True + + # TODO: WHY use posix?? + bdir = to_posix(base_dir) + + errors, abouts = load_inventory( + location=location, + base_dir=bdir, + reference_dir=reference_dir, + scancode=scancode, + worksheet=worksheet + ) + if gen_license: + license_dict, err = model.pre_process_and_fetch_license_dict( + abouts, api_url=api_url, api_key=api_key) + if err: + for e in err: + # Avoid having same error multiple times + if not e in errors: + errors.append(e) + + for about in abouts: + # Strip trailing spaces + about.about_file_path = about.about_file_path.strip() + if about.about_file_path.startswith('/'): + about.about_file_path = about.about_file_path.lstrip('/') + # Use the name as the ABOUT file name if about_resource is empty + if not about.about_file_path: + about.about_file_path = about.name.value + dump_loc = join(bdir, about.about_file_path.lstrip('/')) + + # The following code is to check if there is any directory ends with spaces + split_path = about.about_file_path.split('/') + dir_endswith_space = False + for segment in split_path: + if segment.endswith(' '): + msg = (u'File path : ' + u'%(dump_loc)s ' + u'contains directory name ends with spaces which is not ' + u'allowed. Generation skipped.' % locals()) + errors.append(Error(ERROR, msg)) + dir_endswith_space = True + break + if dir_endswith_space: + # Continue to work on the next about object + continue + + try: + + licenses_dict = {} + if gen_license: + # Write generated LICENSE file + license_key_name_context_url_list = about.dump_lic( + dump_loc, license_dict) + if license_key_name_context_url_list: + for lic_key, lic_name, lic_filename, lic_context, lic_url, spdx_lic_key in license_key_name_context_url_list: + licenses_dict[lic_key] = [ + lic_name, lic_filename, lic_context, lic_url, spdx_lic_key] + if not lic_name in about.license_name.value: + about.license_name.value.append(lic_name) + about.license_file.value[lic_filename] = lic_filename + if not lic_url in about.license_url.value: + about.license_url.value.append(lic_url) + if not spdx_lic_key in about.spdx_license_key.value: + about.spdx_license_key.value.append(spdx_lic_key) + if about.license_name.value: + about.license_name.present = True + if about.license_file.value: + about.license_file.present = True + if about.license_url.value: + about.license_url.present = True + if about.spdx_license_key.value: + about.spdx_license_key.present = True + + about.dump(dump_loc, licenses_dict) + + if android: + """ + Create MODULE_LICENSE_XXX and get context to create NOTICE file + follow the standard from Android Open Source Project + """ + import os + parent_path = os.path.dirname(util.to_posix(dump_loc)) + + about.android_module_license(parent_path) + notice_path, notice_context = about.android_notice(parent_path) + if notice_path in notice_dict.keys(): + notice_dict[notice_path] += '\n\n' + notice_context + else: + notice_dict[notice_path] = notice_context + + except Exception as e: + # only keep the first 100 char of the exception + # TODO: truncated errors are likely making diagnotics harder + emsg = repr(e)[:100] + msg = (u'Failed to write .ABOUT file at : ' + u'%(dump_loc)s ' + u'with error: %(emsg)s' % locals()) + errors.append(Error(ERROR, msg)) + + if android: + # Check if there is already a NOTICE file present + for path in notice_dict.keys(): + if os.path.exists(path): + msg = (u'NOTICE file already exist at: %s' % path) + errors.append(Error(ERROR, msg)) + else: + about.dump_android_notice(path, notice_dict[path]) + return errors, abouts diff --git a/.venv/lib/python3.11/site-packages/attributecode/licenses.py b/.venv/lib/python3.11/site-packages/attributecode/licenses.py new file mode 100644 index 00000000..9280ab84 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/licenses.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +# Common license keys +COMMON_LICENSES = ( + 'aes-128-3.0', + 'agpl-3.0-plus', + 'apache-1.1', + 'apache-2.0', + 'apple-attribution-1997', + 'apple-excl', + 'apsl-2.0', + 'arphic-public', + 'artistic-perl-1.0', + 'artistic-2.0', + 'bitstream', + 'boost-1.0', + 'broadcom-cfe', + 'bsd-new', + 'bsd-original', + 'bsd-original-uc', + 'bsd-simplified', + 'cmu-computing-services', + 'cddl-1.0', + 'cddl-1.1', + 'cpl-1.0', + 'cc-by-2.5', + 'cc-by-sa-3.0', + 'curl', + 'freetype', + 'gpl-1.0-plus', + 'gpl-2.0', + 'gpl-2.0-bison', + 'gpl-2.0-glibc', + 'gpl-2.0-plus', + 'gpl-3.0', + 'gpl-3.0-plus', + 'lgpl-2.0', + 'lgpl-2.0-plus', + 'lgpl-2.1', + 'lgpl-2.1-plus', + 'lgpl-3.0', + 'lgpl-3.0-plus', + 'gpl-2.0-plus-linking', + 'gpl-2.0-broadcom-linking', + 'ijg', + 'isc', + 'larabie', + 'libpng', + 'ms-limited-public', + 'ms-pl', + 'ms-rl', + 'ms-ttf-eula', + 'mit', + 'mpl-1.1', + 'mpl-2.0', + 'net-snmp', + 'npl-1.1', + 'ntpl', + 'openssl-ssleay', + 'ssleay-windows', + 'rsa-md4', + 'rsa-md5', + 'sfl-license', + 'sgi-freeb-2.0', + 'sun-rpc', + 'tcl', + 'tidy', + 'uoi-ncsa', + 'x11', + 'zlib', +) diff --git a/.venv/lib/python3.11/site-packages/attributecode/model.py b/.venv/lib/python3.11/site-packages/attributecode/model.py new file mode 100644 index 00000000..e935af2b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/model.py @@ -0,0 +1,2184 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +""" +AboutCode toolkit is a tool to process ABOUT files. ABOUT files are +small text files that document the provenance (aka. the origin and +license) of software components as well as the essential obligation +such as attribution/credits and source code redistribution. See the +ABOUT spec at http://dejacode.org. + +AboutCode toolkit reads and validates ABOUT files and collect software +components inventories. +""" + +import json +import os +import posixpath +from requests import get, head, exceptions +import sys +import traceback +from itertools import zip_longest + +from urllib.parse import urljoin +from urllib.parse import urlparse + +from license_expression import Licensing +from packageurl import PackageURL + +from attributecode import __version__ +from attributecode import CRITICAL +from attributecode import ERROR +from attributecode import INFO +from attributecode import WARNING +from attributecode import api +from attributecode import Error +from attributecode import saneyaml +from attributecode import gen +from attributecode import util +from attributecode.transform import write_excel +from attributecode.util import add_unc +from attributecode.util import boolean_fields +from attributecode.util import copy_license_notice_files +from attributecode.util import copy_file +from attributecode.util import csv +from attributecode.util import file_fields +from attributecode.util import filter_errors +from attributecode.util import get_spdx_key_and_lic_key_from_licdb +from attributecode.util import is_valid_name +from attributecode.util import on_windows +from attributecode.util import norm +from attributecode.util import replace_tab_with_spaces +from attributecode.util import wrap_boolean_value +from attributecode.util import UNC_PREFIX +from attributecode.util import ungroup_licenses +from attributecode.util import ungroup_licenses_from_sctk + +genereated_tk_version = "# Generated with AboutCode Toolkit Version %s \n\n" % __version__ + + +class Field(object): + """ + An ABOUT file field. The initial value is a string. Subclasses can and + will alter the value type as needed. + """ + + def __init__(self, name=None, value=None, required=False, present=False): + # normalized names are lowercased per specification + self.name = name + # save this and do not mutate it afterwards + if isinstance(value, str): + self.original_value = value + # elif value: + # Don't convert it to string. Leave the format as-is + # self.original_value = repr(value) + else: + self.original_value = value + + # can become a string, list or OrderedDict() after validation + self.value = value or self.default_value() + + self.required = required + # True if the field is present in an About object + self.present = present + + self.errors = [] + + def default_value(self): + return '' + + def validate(self, *args, **kwargs): + """ + Validate and normalize thyself. Return a list of errors. + """ + errors = [] + name = self.name + + self.value = self.default_value() + if not self.present: + # required fields must be present + if self.required: + msg = u'Field %(name)s is required' + errors.append(Error(CRITICAL, msg % locals())) + return errors + else: + # present fields should have content ... + # The boolean value can be True, False and None + # The value True or False is the content of boolean fields + if not name in boolean_fields and not self.has_content: + # ... especially if required + if self.required: + msg = u'Field %(name)s is required and empty' + severity = CRITICAL + else: + severity = INFO + msg = u'Field %(name)s is present but empty.' + errors.append(Error(severity, msg % locals())) + else: + # present fields with content go through validation... + # first trim any trailing spaces on each line + if isinstance(self.original_value, str): + value = '\n'.join(s.rstrip() for s + in self.original_value.splitlines(False)) + # then strip leading and trailing spaces + value = value.strip() + else: + value = self.original_value + self.value = value + try: + validation_errors = self._validate(*args, **kwargs) + errors.extend(validation_errors) + except Exception as e: + emsg = repr(e) + msg = u'Error validating field %(name)s: %(value)r: %(emsg)r' + errors.append(Error(CRITICAL, msg % locals())) + raise + + # set or reset self + self.errors = errors + return errors + + def _validate(self, *args, **kwargs): + """ + Validate and normalize thyself. Return a list of errors. + Subclasses should override as needed. + """ + return [] + + def _serialized_value(self): + return self.value if self.value else u'' + + def serialize(self): + """ + Return a unicode serialization of self in the ABOUT format. + """ + name = self.name + value = self.serialized_value() or u'' + if self.has_content or self.value: + value = value.splitlines(True) + # multi-line + if len(value) > 1: + # This code is used to read the YAML's multi-line format in + # ABOUT files + # (Test: test_loads_dumps_is_idempotent) + if value[0].strip() == u'|' or value[0].strip() == u'>': + value = u' '.join(value) + else: + # Insert '|' as the indicator for multi-line follow by a + # newline character + value.insert(0, u'|\n') + # insert 4 spaces for newline values + value = u' '.join(value) + else: + # FIXME: See https://github.com/nexB/aboutcode-toolkit/issues/323 + # The yaml.load() will throw error if the parsed value + # contains ': ' character. A work around is to put a pipe, '|' + # to indicate the whole value as a string + if value and ': ' in value[0]: + value.insert(0, u'|\n') + # insert 4 spaces for newline values + value = u' '.join(value) + else: + value = u''.join(value) + + serialized = u'%(name)s:' % locals() + if value: + serialized += ' ' + '%(value)s' % locals() + return serialized + + def serialized_value(self): + """ + Return a unicode serialization of self in the ABOUT format. + Does not include a white space for continuations. + """ + return self._serialized_value() or u'' + + @property + def has_content(self): + return self.original_value + + def __repr__(self): + name = self.name + value = self.value + required = self.required + has_content = self.has_content + present = self.present + r = ('Field(name=%(name)r, value=%(value)r, required=%(required)r, present=%(present)r)') + return r % locals() + + def __eq__(self, other): + """ + Equality based on string content value, ignoring spaces. + """ + return (isinstance(other, self.__class__) + and self.name == other.name + and self.value == other.value) + + +class StringField(Field): + """ + A field containing a string value possibly on multiple lines. + The validated value is a string. + """ + + def _validate(self, *args, **kwargs): + errors = super(StringField, self)._validate(*args, ** kwargs) + no_special_char_field = [ + 'license_expression', 'license_key', 'license_name', 'declared_license_expression', 'other_license_expression '] + name = self.name + if name in no_special_char_field: + val = self.value + special_char = detect_special_char(val) + if special_char: + msg = (u'The following character(s) cannot be in the %(name)s: ' + '%(special_char)r' % locals()) + errors.append(Error(ERROR, msg)) + return errors + + def _serialized_value(self): + return self.value if self.value else u'' + + def __eq__(self, other): + """ + Equality based on string content value, ignoring spaces + """ + if not (isinstance(other, self.__class__) + and self.name == other.name): + return False + + if self.value == other.value: + return True + + # compare values stripped from spaces. Empty and None are equal + if self.value: + sval = u''.join(self.value.split()) + if not sval: + sval = None + + if other.value: + oval = u''.join(other.value.split()) + if not oval: + oval = None + + if sval == oval: + return True + + +class SingleLineField(StringField): + """ + A field containing a string value on a single line. The validated value is + a string. + """ + + def _validate(self, *args, **kwargs): + errors = super(SingleLineField, self)._validate(*args, ** kwargs) + if self.value and isinstance(self.value, str) and '\n' in self.value: + name = self.name + value = self.original_value + msg = (u'Field %(name)s: Cannot span multiple lines: %(value)s' + % locals()) + errors.append(Error(ERROR, msg)) + return errors + + +class ListField(StringField): + """ + A field containing a list of string values, one per line. The validated + value is a list. + """ + + def default_value(self): + return [] + + def _validate(self, *args, **kwargs): + errors = super(ListField, self)._validate(*args, ** kwargs) + + # reset + self.value = [] + + if isinstance(self.original_value, str): + values = self.original_value.splitlines(False) + elif isinstance(self.original_value, list): + values = self.original_value + else: + values = [repr(self.original_value)] + + for val in values: + if isinstance(val, str): + val = val.strip() + if not val: + name = self.name + msg = (u'Field %(name)s: ignored empty list value' + % locals()) + errors.append(Error(INFO, msg)) + continue + # keep only unique and report error for duplicates + if val not in self.value: + self.value.append(val) + else: + name = self.name + msg = (u'Field %(name)s: ignored duplicated list value: ' + '%(val)r' % locals()) + errors.append(Error(WARNING, msg)) + return errors + + def _serialized_value(self): + return self.value if self.value else u'' + + def __eq__(self, other): + """ + Equality based on sort-insensitive values + """ + + if not (isinstance(other, self.__class__) + and self.name == other.name): + return False + + if self.value == other.value: + return True + + # compare values stripped from spaces. + sval = [] + if self.value and isinstance(self.value, list): + sval = sorted(self.value) + + oval = [] + if other.value and isinstance(other.value, list): + oval = sorted(other.value) + + if sval == oval: + return True + + +class PackageUrlField(StringField): + """ + A Package URL field. The validated value is a purl. + """ + + def _validate(self, *args, **kwargs): + """ + Check that Package URL is valid. Return a list of errors. + """ + errors = super(PackageUrlField, self)._validate(*args, ** kwargs) + name = self.name + val = self.value + if not self.is_valid_purl(val): + msg = (u'Field %(name)s: Invalid Package URL: %(val)s' % locals()) + errors.append(Error(WARNING, msg)) + return errors + + @staticmethod + def is_valid_purl(purl): + """ + Return True if a Package URL is valid. + """ + try: + return bool(PackageURL.from_string(purl)) + except: + return False + + +class UrlListField(ListField): + """ + A URL field. The validated value is a list of URLs. + """ + + def _validate(self, *args, **kwargs): + """ + Check that URLs are valid. Return a list of errors. + """ + errors = super(UrlListField, self)._validate(*args, ** kwargs) + name = self.name + val = self.value + for url in val: + if not self.is_valid_url(url): + msg = (u'Field %(name)s: Invalid URL: %(val)s' % locals()) + errors.append(Error(WARNING, msg)) + return errors + + @staticmethod + def is_valid_url(url): + """ + Return True if a URL is valid. + """ + scheme, netloc, _path, _p, _q, _frg = urlparse(url) + valid = scheme in ('http', 'https', 'ftp') and netloc + return valid + + +class UrlField(StringField): + """ + A URL field. The validated value is a URL. + """ + + def _validate(self, *args, **kwargs): + """ + Check that URL is valid. Return a list of errors. + """ + errors = super(UrlField, self)._validate(*args, ** kwargs) + name = self.name + val = self.value + if not self.is_valid_url(val): + msg = (u'Field %(name)s: Invalid URL: %(val)s' % locals()) + errors.append(Error(WARNING, msg)) + return errors + + @staticmethod + def is_valid_url(url): + """ + Return True if a URL is valid. + """ + scheme, netloc, _path, _p, _q, _frg = urlparse(url) + valid = scheme in ('http', 'https', 'ftp') and netloc + return valid + + +class PathField(ListField): + """ + A field pointing to one or more paths relative to the ABOUT file location. + The validated value is an ordered dict of path->location or None. + The paths can also be resolved + """ + + def default_value(self): + return {} + + def _validate(self, *args, **kwargs): + """ + Ensure that paths point to existing resources. Normalize to posix + paths. Return a list of errors. + + base_dir is the directory location of the ABOUT file used to resolve + relative paths to actual file locations. + """ + errors = super(PathField, self)._validate(*args, ** kwargs) + self.about_file_path = kwargs.get('about_file_path') + self.running_inventory = kwargs.get('running_inventory') + self.base_dir = kwargs.get('base_dir') + self.reference_dir = kwargs.get('reference_dir') + + if self.base_dir: + self.base_dir = util.to_posix(self.base_dir) + + name = self.name + + # Why are paths a dict? + # Ans: The reason why the PathField use a dict is because + # for the FileTextField, the key is used as the path to the file and + # the value is used as the context of the file + # dict of normalized paths to a location or None + paths = {} + + for path_value in self.value: + p = path_value.split(',') + for path in p: + path = path.strip() + path = util.to_posix(path) + + # normalize eventual / to . + # and a succession of one or more ////// to . too + if path.strip() and not path.strip(posixpath.sep): + path = '.' + + # removing leading and trailing path separator + # path are always relative + path = path.strip(posixpath.sep) + + # the license files, if need to be copied, are located under the path + # set from the 'license-text-location' option, so the tool should check + # at the 'license-text-location' instead of the 'base_dir' + if not (self.base_dir or self.reference_dir): + msg = (u'Field %(name)s: Unable to verify path: %(path)s:' + u' No base directory provided' % locals()) + errors.append(Error(ERROR, msg)) + location = None + paths[path] = location + continue + if self.reference_dir: + location = posixpath.join(self.reference_dir, path) + else: + # The 'about_resource' should be a joined path with + # the 'about_file_path' and the 'base_dir + if not self.running_inventory and self.about_file_path: + # Get the parent directory of the 'about_file_path' + afp_parent = posixpath.dirname(self.about_file_path) + + # Create a relative 'about_resource' path by joining the + # parent of the 'about_file_path' with the value of the + # 'about_resource' + arp = posixpath.join(afp_parent, path) + normalized_arp = posixpath.normpath( + arp).strip(posixpath.sep) + location = posixpath.join( + self.base_dir, normalized_arp) + else: + location = posixpath.normpath( + posixpath.join(self.base_dir, path)) + + location = util.to_native(location) + location = os.path.abspath(os.path.normpath(location)) + location = util.to_posix(location) + location = add_unc(location) + + if not os.path.exists(location): + # We don't want to show the UNC_PREFIX in the error message + location = util.to_posix(location.strip(UNC_PREFIX)) + msg = (u'Field %(name)s: Path %(location)s not found' + % locals()) + # We want to show INFO error for 'about_resource' + if name == u'about_resource': + errors.append(Error(INFO, msg)) + else: + errors.append(Error(CRITICAL, msg)) + location = None + + paths[path] = location + + self.value = paths + return errors + + +class AboutResourceField(PathField): + """ + Special field for about_resource. self.resolved_paths contains a list of + the paths resolved relative to the about file path. + """ + + def __init__(self, *args, ** kwargs): + super(AboutResourceField, self).__init__(*args, ** kwargs) + self.resolved_paths = [] + + def _validate(self, *args, **kwargs): + errors = super(AboutResourceField, self)._validate(*args, ** kwargs) + return errors + + +class IgnoredResourcesField(PathField): + """ + Special field for ignored_resources. self.ignored_paths contains a list of + path patterns (glob patterns) which are not part of the summarization provided + by the ABOUT file. + """ + + def __init__(self, *args, ** kwargs): + super(AboutResourceField, self).__init__(*args, ** kwargs) + self.resolved_paths = [] + + def _validate(self, *args, **kwargs): + errors = super(AboutResourceField, self)._validate(*args, ** kwargs) + return errors + + +class FileTextField(PathField): + """ + A path field pointing to one or more text files such as license files. + The validated value is an ordered dict of path->Text or None if no + location or text could not be loaded. + """ + + def _validate(self, *args, **kwargs): + """ + Load and validate the texts referenced by paths fields. Return a list + of errors. base_dir is the directory used to resolve a file location + from a path. + """ + errors = super(FileTextField, self)._validate(*args, ** kwargs) + # a FileTextField is a PathField + # self.value is a paths to location ordered dict + # we will replace the location with the text content + name = self.name + for path, location in self.value.items(): + if not location: + # do not try to load if no location + # errors about non existing locations are PathField errors + # already collected. + continue + try: + # TODO: we have lots the location by replacing it with a text + location = add_unc(location) + with open(location, encoding='utf-8', errors='replace') as txt: + text = txt.read() + self.value[path] = text + except Exception as e: + # only keep the first 100 char of the exception + emsg = repr(e)[:100] + msg = (u'Field %(name)s: Failed to load text at path: ' + u'%(path)s ' + u'with error: %(emsg)s' % locals()) + errors.append(Error(ERROR, msg)) + # set or reset self + self.errors = errors + return errors + + +class BooleanField(SingleLineField): + """ + An flag field with a boolean value. Validated value is False, True or None. + """ + + def default_value(self): + return None + + true_flags = ('yes', 'y', 'true', 'x') + false_flags = ('no', 'n', 'false') + flag_values = true_flags + false_flags + + def _validate(self, *args, **kwargs): + """ + Check that flag are valid. Convert flags to booleans. Default flag to + False. Return a list of errors. + """ + errors = super(BooleanField, self)._validate(*args, ** kwargs) + self.about_file_path = kwargs.get('about_file_path') + flag = self.get_flag(self.original_value) + if flag is False: + name = self.name + val = self.original_value + about_file_path = self.about_file_path + flag_values = self.flag_values + msg = (u'Path: %(about_file_path)s - Field %(name)s: Invalid flag value: %(val)r is not ' + u'one of: %(flag_values)s' % locals()) + errors.append(Error(ERROR, msg)) + self.value = None + elif flag is None: + name = self.name + msg = (u'Field %(name)s: field is present but empty. ' % locals()) + errors.append(Error(INFO, msg)) + self.value = None + else: + if flag == u'yes' or flag is True: + self.value = True + else: + self.value = False + return errors + + def get_flag(self, value): + """ + Return a normalized existing flag value if found in the list of + possible values or None if empty or False if not found or original value + if it is not a boolean value + """ + if value is None or value == '': + return None + + if isinstance(value, bool): + return value + else: + if isinstance(value, str): + value = value.strip() + if not value: + return None + + value = value.lower() + if value in self.flag_values: + if value in self.true_flags: + return u'yes' + else: + return u'no' + else: + return False + else: + return False + + @property + def has_content(self): + """ + Return true if it has content regardless of what value, False otherwise + """ + if self.original_value: + return True + return False + + def _serialized_value(self): + # default normalized values for serialization + if self.value: + return u'yes' + elif self.value is False: + return u'no' + else: + # self.value is None + # TODO: should we serialize to No for None??? + return u'' + + def __eq__(self, other): + """ + Boolean equality + """ + return (isinstance(other, self.__class__) + and self.name == other.name + and self.value == other.value) + + +class BooleanAndTwoCharactersField(SingleLineField): + """ + Field with either a boolean value or character(s) value (at most 2 + characters). Validated value is False, True, None or character value. + """ + + def default_value(self): + return None + + true_flags = ('yes', 'y', 'true', 'x') + false_flags = ('no', 'n', 'false') + flag_values = true_flags + false_flags + + def _validate(self, *args, **kwargs): + """ + Check that flag are valid with either boolean value or character value. + Default flag to False. Return a list of errors. + """ + errors = super(BooleanAndTwoCharactersField, + self)._validate(*args, ** kwargs) + self.about_file_path = kwargs.get('about_file_path') + flag = self.get_value(self.original_value) + if flag is False: + name = self.name + val = self.original_value + about_file_path = self.about_file_path + flag_values = self.flag_values + msg = (u'Path: %(about_file_path)s - Field %(name)s: Invalid value: %(val)r is not ' + u'one of: %(flag_values)s and it is not a 1 or 2 character value.' % locals()) + errors.append(Error(ERROR, msg)) + self.value = None + elif flag is None: + name = self.name + msg = (u'Field %(name)s: field is present but empty. ' % locals()) + errors.append(Error(INFO, msg)) + self.value = None + else: + if flag == u'yes' or flag is True: + self.value = True + elif flag == u'no': + self.value = False + else: + self.value = flag + return errors + + def get_value(self, value): + """ + Return a normalized existing value if found in the list of + possible values or None if empty or False if not found or original value + if it is not a boolean value + """ + if value is None or value == '': + return None + + if isinstance(value, bool): + return value + else: + if isinstance(value, str): + value = value.strip() + if not value: + return None + + value = value.lower() + if value in self.flag_values or len(value) <= 2: + if value in self.true_flags: + return u'yes' + elif value in self.false_flags: + return u'no' + else: + return value + else: + return False + else: + return False + + @property + def has_content(self): + """ + Return true if it has content regardless of what value, False otherwise + """ + if self.original_value: + return True + return False + + def _serialized_value(self): + # default normalized values for serialization + if self.value: + if isinstance(self.value, bool): + return u'yes' + else: + return self.value + elif self.value is False: + return u'no' + else: + # self.value is None + # TODO: should we serialize to No for None??? + return u'' + + +def validate_fields(fields, about_file_path, running_inventory, base_dir, + reference_dir=None): + """ + Validate a sequence of Field objects. Return a list of errors. + Validation may update the Field objects as needed as a side effect. + """ + errors = [] + for f in fields: + val_err = f.validate( + base_dir=base_dir, + about_file_path=about_file_path, + running_inventory=running_inventory, + reference_dir=reference_dir, + ) + errors.extend(val_err) + return errors + + +def validate_field_name(name): + if not is_valid_name(name): + msg = ('Field name: %(name)r contains illegal name characters ' + '(or empty spaces) and is ignored.') + return Error(WARNING, msg % locals()) + + +class License: + """ + Represent a License object + """ + + def __init__(self, key, name, filename, url, text): + self.key = key + self.name = name + self.filename = filename + self.url = url + self.text = text + + +class About(object): + """ + Represent an ABOUT file and functions to parse and validate a file. + """ + # special names, used only when serializing lists of ABOUT files to CSV or + # similar + + # name of the attribute containing the relative ABOUT file path + ABOUT_FILE_PATH_ATTR = 'about_file_path' + + # name of the attribute containing the resolved relative Resources paths + about_resource_path_attr = 'about_resource_path' + + # name of the attribute containing the resolved relative Resources paths + ABOUT_RESOURCE_ATTR = 'about_resource' + + # Required fields + required_fields = ['name'] + + def get_required_fields(self): + return [f for f in self.fields if f.required] + + def set_standard_fields(self): + """ + Create fields in an ordered dict to keep a standard ordering. We + could use a metaclass to track ordering django-like but this approach + is simpler. + """ + self.fields = dict([ + ('about_resource', AboutResourceField()), + ('ignored_resources', AboutResourceField()), + ('name', SingleLineField(required=True)), + ('version', SingleLineField()), + + ('download_url', UrlField()), + ('description', StringField()), + ('homepage_url', UrlField()), + ('package_url', PackageUrlField()), + ('notes', StringField()), + + ('license_expression', SingleLineField()), + ('license_key', ListField()), + ('license_name', ListField()), + ('license_file', FileTextField()), + ('license_url', UrlListField()), + ('spdx_license_expression', SingleLineField()), + ('spdx_license_key', ListField()), + ('declared_license_expression', SingleLineField()), + ('other_license_expression', SingleLineField()), + ('copyright', StringField()), + ('notice_file', FileTextField()), + ('notice_url', UrlField()), + + ('redistribute', BooleanField()), + ('attribute', BooleanAndTwoCharactersField()), + ('track_changes', BooleanField()), + ('modified', BooleanField()), + ('internal_use_only', BooleanField()), + + ('changelog_file', FileTextField()), + + ('owner', StringField()), + ('owner_url', UrlField()), + ('contact', StringField()), + ('author', StringField()), + ('author_file', FileTextField()), + + ('vcs_tool', SingleLineField()), + ('vcs_repository', SingleLineField()), + ('vcs_path', SingleLineField()), + ('vcs_tag', SingleLineField()), + ('vcs_branch', SingleLineField()), + ('vcs_revision', SingleLineField()), + + ('checksum_md5', SingleLineField()), + ('checksum_sha1', SingleLineField()), + ('checksum_sha256', SingleLineField()), + ('spec_version', SingleLineField()), + ]) + + for name, field in self.fields.items(): + # we could have a hack to get the actual field name + # but setting an attribute is explicit and cleaner + field.name = name + setattr(self, name, field) + + def __init__(self, location=None, about_file_path=None, strict=False): + """ + Create an instance. + If strict is True, raise an Exception on errors. Otherwise the errors + attribute contains the errors. + """ + self.set_standard_fields() + self.custom_fields = {} + + self.errors = [] + + # about file path relative to the root of an inventory using posix + # path separators + self.about_file_path = about_file_path + + # os native absolute location, using posix path separators + self.location = location + self.base_dir = None + if self.location: + self.base_dir = os.path.dirname(location) + self.errors.extend(self.load(location)) + if strict and self.errors and filter_errors(self.errors): + msg = '\n'.join(map(str, self.errors)) + raise Exception(msg) + + def __repr__(self): + return repr(self.all_fields()) + + def __eq__(self, other): + """ + Equality based on fields and custom_fields., i.e. content. + """ + return (isinstance(other, self.__class__) + and self.fields == other.fields + and self.custom_fields == other.custom_fields) + + def all_fields(self): + """ + Return the list of all Field objects. + """ + return list(self.fields.values()) + list(self.custom_fields.values()) + + def as_dict(self): + """ + Return all the standard fields and customer-defined fields of this + About object in an ordered dict. + """ + data = {} + data[self.ABOUT_FILE_PATH_ATTR] = self.about_file_path + with_values = ((fld.name, fld.serialized_value()) + for fld in self.all_fields()) + non_empty = ((name, value) for name, value in with_values if value) + data.update(non_empty) + return data + + def hydrate(self, fields): + """ + Process an iterable of field (name, value) tuples. Update or create + Fields attributes and the fields and custom fields dictionaries. + Return a list of errors. + """ + errors = [] + seen_fields = {} + illegal_name_list = [] + + for name, value in fields: + orig_name = name + name = name.lower() + + # Some special attributes + if name == self.ABOUT_FILE_PATH_ATTR: + # this is a special attribute set directly on object + setattr(self, name, value) + continue + + if name == self.about_resource_path_attr: + # this is a special attribute, skip entirely + continue + + # A field that has been already processed ... and has a value + previous_value = seen_fields.get(name) + if previous_value: + if value != previous_value: + msg = (u'Field %(orig_name)s is a duplicate. ' + u'Original value: "%(previous_value)s" ' + u'replaced with: "%(value)s"') + errors.append(Error(WARNING, msg % locals())) + continue + + seen_fields[name] = value + + # A standard field (could be essential/required or not) + standard_field = self.fields.get(name) + if standard_field: + standard_field.original_value = value + standard_field.value = value + standard_field.present = True + continue + + # A custom field + # is the name valid? + if not is_valid_name(name): + if not name in illegal_name_list: + illegal_name_list.append(name) + continue + + msg = 'Custom Field: %(orig_name)s' + errors.append(Error(INFO, msg % locals())) + # is this a known one? + custom_field = self.custom_fields.get(name) + if custom_field: + # An known custom field + custom_field.original_value = value + custom_field.value = value + custom_field.present = True + else: + # A new, unknown custom field + custom_field = Field(name=name, value=value, present=True) + self.custom_fields[name] = custom_field + # FIXME: why would this ever fail??? + try: + if name in dir(self): + raise Exception( + 'Illegal field: %(name)r: %(value)r.' % locals()) + setattr(self, name, custom_field) + except: + msg = 'Internal error with custom field: %(name)r: %(value)r.' + errors.append(Error(CRITICAL, msg % locals())) + + if illegal_name_list: + msg = ('Field name: %(illegal_name_list)r contains illegal name characters ' + '(or empty spaces) and is ignored.') + errors.append(Error(WARNING, msg % locals())) + return errors + + def process(self, fields, about_file_path, running_inventory=False, + base_dir=None, scancode=False, from_attrib=False, reference_dir=None): + """ + Validate and set as attributes on this About object a sequence of + `fields` name/value tuples. Return a list of errors. + """ + self.base_dir = base_dir + self.reference_dir = reference_dir + afp = self.about_file_path + + errors = self.hydrate(fields) + + # We want to copy the license_files before the validation + if reference_dir and not from_attrib: + copy_err = copy_license_notice_files( + fields, base_dir, reference_dir, afp) + errors.extend(copy_err) + + # TODO: why? we validate all fields, not only these hydrated + # The validate functions does not allow duplicated entry for a list meaning + # it will cause problem when using scancode license detection as an input as + # it usually returns duplicated license_key and many license have duplicated + # score such as 100. We need to handle this scenario using different method. + if not scancode: + validation_errors = validate_fields( + self.all_fields(), + about_file_path, + running_inventory, + self.base_dir, + self.reference_dir) + errors.extend(validation_errors) + return errors + + def load(self, location): + """ + Read, parse and process the ABOUT file at `location`. + Return a list of errors and update self with errors. + """ + self.location = location + loc = util.to_posix(location) + base_dir = posixpath.dirname(loc) + errors = [] + try: + loc = add_unc(loc) + with open(loc, encoding='utf-8', errors='replace') as txt: + input_text = txt.read() + if not input_text: + msg = 'ABOUT file is empty: %(location)r' + errors.append(Error(CRITICAL, msg % locals())) + self.errors = errors + return errors + # The 'Yes' and 'No' will be converted to 'True' and 'False' in the yaml.load() + # Therefore, we need to wrap the original value in quote to prevent + # the conversion + pre_input = wrap_boolean_value(input_text) + # saneyaml.load() will have parsing error if the input has + # tab value. Therefore, we should check if the input contains + # any tab and then convert it to spaces. + input = replace_tab_with_spaces(pre_input) + # FIXME: this should be done in the commands, not here + """ + The running_inventory defines if the current process is 'inventory' or not. + This is used for the validation of the path of the 'about_resource'. + In the 'inventory' command, the code will use the parent of the about_file_path + location and join with the 'about_resource' for the validation. + On the other hand, in the 'gen' command, the code will use the + generated location (aka base_dir) along with the parent of the about_file_path + and then join with the 'about_resource' + """ + running_inventory = True + data = saneyaml.load(input, allow_duplicate_keys=False) + errs = self.load_dict( + data, base_dir, running_inventory=running_inventory) + errors.extend(errs) + except Exception as e: + # The trace is good for debugging, but probably not good for user to + # see the traceback message + # trace = traceback.format_exc() + # msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r\n%(trace)s' + msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r' + errors.append(Error(CRITICAL, msg % locals())) + + self.errors = errors + return errors + + # FIXME: should be a from_dict class factory instead + # FIXME: running_inventory: remove this : this should be done in the commands, not here + + def load_dict(self, fields_dict, base_dir, scancode=False, from_attrib=False, running_inventory=False, reference_dir=None,): + """ + Load this About object file from a `fields_dict` name/value dict. + Return a list of errors. + """ + # do not keep empty + fields = list(fields_dict.items()) + + if scancode: + have_copyright = False + for key, value in fields: + if not value: + continue + if key == u'copyrights': + have_copyright = True + elif key == u'license_detections': + lic_list = ungroup_licenses_from_sctk(value) + lic_exp_list = [] + for detected_license in value: + if 'license_expression' in detected_license: + lic_exp_list.append( + detected_license['license_expression']) + if lic_exp_list: + fields.append( + ('license_expression', ' AND '.join(lic_exp_list))) + + lic_key_list = [] + lic_key_exp_list = [] + lic_score_list = [] + for lic in lic_list: + _char, lic_keys, _invalid_lic_exp = parse_license_expression( + lic['lic_exp']) + lic_key_list.append(lic_keys) + # for lic_key in lic_keys: + # lic_key_list.append([lic_key]) + + for lic in lic_list: + lic_key_exp_list.append(lic['lic_exp']) + lic_score_list.append(lic['score']) + fields.append(('license_key', lic_key_list)) + fields.append(('license_key_expression', lic_key_exp_list)) + fields.append(('license_score', lic_score_list)) + + # The licenses field has been ungrouped and can be removed. + # Otherwise, it will gives the following INFO level error + # 'Field licenses is a custom field.' + licenses_field = (key, value) + fields.remove(licenses_field) + # Make sure the copyrights is present even is empty to avoid error + # when generating with Jinja + if not have_copyright: + fields.append(('copyrights', '')) + + else: + for key, value in fields: + if not value: + # never return empty or absent fields + continue + if key == u'licenses': + # FIXME: use a license object instead + lic_key, lic_name, lic_file, lic_url, spdx_lic_key, lic_score, lic_matched_text = ungroup_licenses( + value) + if lic_key: + fields.append(('license_key', lic_key)) + if lic_name: + fields.append(('license_name', lic_name)) + if lic_file: + fields.append(('license_file', lic_file)) + if lic_url: + fields.append(('license_url', lic_url)) + if spdx_lic_key: + fields.append(('spdx_license_key', spdx_lic_key)) + # The license score is a key from scancode license scan + if lic_score: + fields.append(('license_score', lic_score)) + if lic_matched_text: + fields.append(('matched_text', lic_matched_text)) + # The licenses field has been ungrouped and can be removed. + # Otherwise, it will gives the following INFO level error + # 'Field licenses is a custom field.' + licenses_field = (key, value) + fields.remove(licenses_field) + + errors = self.process( + fields=fields, + about_file_path=self.about_file_path, + running_inventory=running_inventory, + base_dir=base_dir, + scancode=scancode, + from_attrib=from_attrib, + reference_dir=reference_dir, + ) + self.errors = errors + return errors + + @classmethod + def from_dict(cls, about_data, base_dir=''): + """ + Return an About object loaded from a python dict. + """ + about = cls() + about.load_dict(about_data, base_dir=base_dir) + return about + + def dumps(self, licenses_dict=None): + """ + Return self as a formatted ABOUT string. + """ + data = {} + # Group the same license information (name, url, file) together + license_key = [] + license_name = [] + license_file = [] + license_url = [] + spdx_license_key = [] + bool_fields = ['redistribute', 'attribute', + 'track_changes', 'modified', 'internal_use_only'] + for field in self.all_fields(): + if not field.value and not field.name in bool_fields: + continue + if field.name == 'license_key' and field.value: + license_key = field.value + elif field.name == 'license_name' and field.value: + license_name = field.value + elif field.name == 'license_file' and field.value: + # Restore the original_value as it was parsed for + # validation purpose + if field.original_value: + # This line break is for the components that have multiple license + # values in CSV format. + if '\n' in field.original_value: + license_file_list = field.original_value.split('\n') + license_file = [] + # Strip the carriage return character '\r' See #443 + for lic in license_file_list: + if '\r' in lic: + license_file.append(lic.strip('\r')) + else: + license_file.append(lic) + else: + if isinstance(field.original_value, list): + license_file = list(field.value.keys()) + else: + # Restore the original license_file value + # See #444 + license_file = [field.original_value] + else: + license_file = list(field.value.keys()) + elif field.name == 'license_url' and field.value: + license_url = field.value + elif field.name == 'spdx_license_key' and field.value: + spdx_license_key = field.value + elif field.name in file_fields and field.value: + data[field.name] = field.original_value + elif field.name in bool_fields and not field.value == None: + data[field.name] = field.value + else: + if field.value: + data[field.name] = field.value + # If there is no license_key value, parse the license_expression + # and get the parsed license key + if 'license_expression' in data: + if not license_key and data['license_expression']: + _spec_char, lic_list, _invalid_lic_exp = parse_license_expression( + data['license_expression']) + license_key = lic_list + + # Group the same license information in a list + # This `licenses_dict` is a dictionary with license key as the key and the + # value is the list of [license_name, license_filename, license_context, license_url] + lic_key_copy = license_key[:] + lic_dict_list = [] + for lic_key in license_key: + lic_dict = {} + if licenses_dict and lic_key in licenses_dict: + lic_dict['key'] = lic_key + lic_name, lic_filename, lic_context, lic_url, spdx_lic_key = licenses_dict[ + lic_key] + if lic_name: + lic_dict['name'] = lic_name + if lic_filename: + lic_dict['file'] = lic_filename + if lic_url: + lic_dict['url'] = lic_url + if spdx_lic_key: + lic_dict['spdx_license_key'] = spdx_lic_key + + # Remove the license information if it has been handled + # The following condition is to check if license information + # has been fetched, the license key is invalid or custom if + # no value for lic_name + if lic_name: + lic_key_copy.remove(lic_key) + if lic_name in license_name: + license_name.remove(lic_name) + if lic_url in license_url: + license_url.remove(lic_url) + if lic_filename in license_file: + license_file.remove(lic_filename) + if spdx_lic_key in spdx_license_key: + spdx_license_key.remove(spdx_lic_key) + lic_dict_list.append(lic_dict) + + # Handle license information that have not been handled. + # If the len of the lic_key is the same as the lic_file, the tool should + # assume the lic_file (custom license) is referring this specific lic_key + # otherwise, the tool shouldn't group them + if len(lic_key_copy) == len(license_file): + license_group = list(zip_longest( + lic_key_copy, license_name, license_file, license_url, spdx_license_key)) + else: + license_group = list(zip_longest( + lic_key_copy, license_name, [], license_url, spdx_license_key)) + # Add the unhandled_lic_file if any + if license_file: + for lic_file in license_file: + license_group.append((None, None, lic_file, None, None)) + + for lic_group in license_group: + lic_dict = {} + if lic_group[0]: + lic_dict['key'] = lic_group[0] + if lic_group[1]: + lic_dict['name'] = lic_group[1] + else: + # If no name is given, treat the key as the name + if lic_group[0]: + lic_dict['name'] = lic_group[0] + if lic_group[2]: + lic_dict['file'] = lic_group[2] + if lic_group[3]: + lic_dict['url'] = lic_group[3] + if lic_group[4]: + lic_dict['spdx_license_key'] = lic_group[4] + lic_dict_list.append(lic_dict) + + # Format the license information in the same order of the license expression + for key in license_key: + for lic_dict in lic_dict_list: + if key == lic_dict['key']: + data.setdefault('licenses', []).append(lic_dict) + lic_dict_list.remove(lic_dict) + break + + for lic_dict in lic_dict_list: + data.setdefault('licenses', []).append(lic_dict) + + return saneyaml.dump(data) + + def dump(self, location, lic_dict=None): + """ + Write formatted ABOUT representation of self to location. + """ + loc = util.to_posix(location) + parent = posixpath.dirname(loc) + + if not posixpath.exists(parent): + os.makedirs(add_unc(parent)) + + about_file_path = loc + if not about_file_path.endswith('.ABOUT'): + # FIXME: we should not infer some location. + if about_file_path.endswith('/'): + about_file_path = util.to_posix( + os.path.join(parent, os.path.basename(parent))) + about_file_path += '.ABOUT' + + if on_windows: + about_file_path = add_unc(about_file_path) + + with open(about_file_path, mode='w', encoding='utf-8', errors='replace') as dumped: + dumped.write(genereated_tk_version) + dumped.write(self.dumps(lic_dict)) + + def dump_android_notice(self, path, context): + """ + Write the NOITCE file consist of copyright, notice and license + """ + if on_windows: + path = add_unc(path) + + with open(path, mode='w', encoding='utf-8', errors='replace') as dumped: + dumped.write(context) + + def android_module_license(self, about_parent_path): + """ + Create MODULE_LICENSE_XXX which the XXX is the value of license key. + """ + for lic_key in self.license_key.value: + # Make uppercase and with dash and spaces and dots replaced by underscore + # just to look similar and consistent. + name = 'MODULE_LICENSE_' + \ + lic_key.replace('.', '_').replace( + '-', '_').replace(' ', '_').upper() + module_lic_path = os.path.join(about_parent_path, name) + # Create an empty MODULE_LICESE_XXX file + open(module_lic_path, 'a').close() + + def android_notice(self, about_parent_path): + """ + Return a notice dictionary which the path of the notice file going + to create will be the key and its context will be the value of the dict. + """ + # Create NOTICE file with the combination context of copyright, + # notice_file and license_file + notice_path = posixpath.join(about_parent_path, 'NOTICE') + notice_context = '' + if self.copyright.value: + notice_context += self.copyright.value + if self.notice_file.value: + notice_file_dict = self.notice_file.value + notice_file_key = notice_file_dict.keys() + for key in notice_file_key: + if notice_file_dict[key]: + notice_context += '\n' + notice_file_dict[key] + '\n' + if self.license_file.value: + lic_file_dict = self.license_file.value + lic_file_key = lic_file_dict.keys() + for key in lic_file_key: + if lic_file_dict[key]: + notice_context += '\n\n' + lic_file_dict[key] + '\n\n' + return notice_path, notice_context + + def dump_lic(self, location, license_dict): + """ + Write LICENSE files and return the a list of key, name, context and the url + as these information are needed for the ABOUT file + """ + license_name = license_context = license_url = '' + loc = util.to_posix(location) + parent = posixpath.dirname(loc) + license_key_name_context_url = [] + + if not posixpath.exists(parent): + os.makedirs(add_unc(parent)) + + licenses_list = [] + if self.license_expression.present: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + self.license_expression.value) + if lic_list: + for lic in lic_list: + if lic not in licenses_list: + licenses_list.append(lic) + if self.declared_license_expression.present: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + self.declared_license_expression.value) + if lic_list: + for lic in lic_list: + if lic not in licenses_list: + licenses_list.append(lic) + if self.other_license_expression.present: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + self.other_license_expression.value) + if lic_list: + for lic in lic_list: + if lic not in licenses_list: + licenses_list.append(lic) + if licenses_list: + self.license_key.value = licenses_list + self.license_key.present = True + for lic_key in licenses_list: + license_name = '' + license_filename = '' + license_context = '' + license_url = '' + spdx_license_key = '' + if lic_key in license_dict: + license_path = posixpath.join(parent, lic_key) + license_path += u'.LICENSE' + license_path = add_unc(license_path) + license_name, license_filename, license_context, license_url, spdx_license_key = license_dict[ + lic_key] + license_info = (lic_key, license_name, license_filename, + license_context, license_url, spdx_license_key) + license_key_name_context_url.append(license_info) + with open(license_path, mode='w', encoding='utf-8', newline='\n', errors='replace') as lic: + lic.write(license_context) + else: + # Invalid license issue is already handled + license_info = (lic_key, license_name, license_filename, + license_context, license_url, spdx_license_key) + license_key_name_context_url.append(license_info) + + return license_key_name_context_url + + +def collect_inventory(location, exclude=None): + """ + Collect ABOUT files at location and return a list of errors and a list of + About objects. + """ + errors = [] + input_location = util.get_absolute(location) + about_locations = list(util.get_about_locations(input_location, exclude)) + + name_errors = util.check_file_names(about_locations) + errors.extend(name_errors) + abouts = [] + custom_fields_list = [] + for about_loc in about_locations: + about_file_path = util.get_relative_path(input_location, about_loc) + about = About(about_loc, about_file_path) + for severity, message in about.errors: + if 'Custom Field' in message: + field_name = message.replace('Custom Field: ', '').strip() + if field_name not in custom_fields_list: + custom_fields_list.append(field_name) + else: + msg = (about_file_path + ": " + message) + errors.append(Error(severity, msg)) + abouts.append(about) + if custom_fields_list: + custom_fields_err_msg = 'Field ' + \ + str(custom_fields_list) + ' is a custom field.' + errors.append(Error(INFO, custom_fields_err_msg)) + return errors, abouts + + +def collect_abouts_license_expression(location): + """ + Read the ABOUT files at location and return a list of ABOUT objects without + validation. The purpose of this is to speed up the process for `gen_license` command. + """ + lic_key_list = [] + errors = [] + input_location = util.get_absolute(location) + about_locations = list(util.get_about_locations(input_location)) + abouts = [] + + for loc in about_locations: + try: + loc = add_unc(loc) + with open(loc, encoding='utf-8', errors='replace') as txt: + input_text = txt.read() + # saneyaml.load() will have parsing error if the input has + # tab value. Therefore, we should check if the input contains + # any tab and then convert it to spaces. + input = replace_tab_with_spaces(input_text) + data = saneyaml.load(input, allow_duplicate_keys=False) + about = About() + about.load_dict(data, base_dir='') + abouts.append(about) + except Exception as e: + trace = traceback.format_exc() + msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r\n%(trace)s' + errors.append(Error(CRITICAL, msg % locals())) + + return errors, abouts + + +def collect_inventory_license_expression(location, scancode=False, worksheet=None): + """ + Read the inventory file at location and return a list of ABOUT objects without + validation. The purpose of this is to speed up the process for `gen_license` command. + """ + abouts = [] + errors = [] + + if scancode: + inventory = gen.load_scancode_json(location) + # ScanCode uses 'detected_license_expression' + if not 'detected_license_expression' in inventory[0]: + errors.append( + Error(CRITICAL, "No 'license_expressions' field in the input.")) + return errors, abouts + else: + if location.endswith('.csv'): + inventory = gen.load_csv(location) + elif location.endswith('.xlsx'): + _dup_cols_err, inventory = gen.load_excel(location, worksheet) + else: + inventory = gen.load_json(location) + # Check if 'license_expression' field is in the input + if not inventory or not 'license_expression' in inventory[0]: + errors.append( + Error(CRITICAL, "No 'license_expression' field in the input.")) + return errors, abouts + + for data in inventory: + about = About() + about.load_dict(data, base_dir='', scancode=scancode) + abouts.append(about) + return errors, abouts + + +def get_field_names(abouts): + """ + Given a list of About objects, return a list of any field names that exist + in any object, including custom fields. + """ + fields = [] + # fields.append(About.ABOUT_FILE_PATH_ATTR) + + standard_fields = About().fields.keys() + standards = [] + for a in abouts: + for name, field in a.fields.items(): + if field.required: + if name not in standards: + standards.append(name) + else: + if field.present: + if name not in standards: + standards.append(name) + # resort standard fields in standard order + # which is a tad complex as this is a predefined order + sorted_std = [] + for fn in standard_fields: + if fn in standards: + sorted_std.append(fn) + fields.extend(sorted_std) + + customs = [] + for a in abouts: + for name, field in a.custom_fields.items(): + if field.has_content: + if name not in customs: + customs.append(name) + # always sort custom fields list by name + customs.sort() + fields.extend(customs) + + return fields + + +def copy_redist_src(copy_list, location, output, with_structure): + """ + Given a list of files/directories and copy to the destination + """ + errors = [] + for from_path in copy_list: + norm_from_path = norm(from_path) + relative_from_path = norm_from_path.partition(util.norm(location))[2] + # Need to strip the '/' to use the join + if relative_from_path.startswith('/'): + relative_from_path = relative_from_path.partition('/')[2] + # Get the directory name of the output path + if with_structure: + output_dir = os.path.dirname(os.path.join( + output, util.norm(relative_from_path))) + else: + output_dir = output + err = copy_file(from_path, output_dir) + if err: + errors.extend(err) + return errors + + +def get_copy_list(abouts, location): + """ + Return a list of files/directories that need to be copied (and error if any) + This is a summary list in a sense that if a directory is already in the list, + its children directories/files will not be included in the list regardless if + they have 'redistribute' flagged. The reason for this is we want to capture + the error/warning if existence files/directories already exist. However, if + we don't have this "summarized" list, and we've copied a file (with directory structure) + and then later on this file's parent directory also need to be copied, then + it will prompt warning as the directory that need to be copied is already exist. + Technically, this is correct, but it leads to confusion. Therefore, we want to + create a summarized list to avoid this kind of confusion. + """ + errors = [] + copy_list = [] + dir_list = [] + file_list = [] + for about in abouts: + if about.redistribute.value: + file_exist = True + for e in about.errors: + if 'Field about_resource' in e.message and 'not found' in e.message: + msg = e.message + u' and cannot be copied.' + errors.append(Error(CRITICAL, msg)) + file_exist = False + continue + if file_exist: + for k in about.about_resource.value: + from_path = about.about_resource.value.get(k) + if on_windows: + norm_from_path = norm(from_path) + else: + norm_from_path = os.path.normpath(from_path) + # Get the relative path + relative_from_path = norm_from_path.partition( + util.norm(location))[2] + if os.path.isdir(from_path): + if not dir_list: + dir_list.append(relative_from_path) + else: + handled = False + for dir in dir_list: + # The dir is a parent of the relative_from_path + if dir in relative_from_path: + handled = True + continue + # The relative_from_path is the parent of the dir + # We need to update the dir_list + if relative_from_path in dir: + dir_list.remove(dir) + dir_list.append(relative_from_path) + handled = True + continue + if not handled: + dir_list.append(relative_from_path) + else: + # Check if the file is from "root" + # If the file is at root level, it'll add to the copy_list + if not os.path.dirname(relative_from_path) == '/': + file_list.append(relative_from_path) + else: + copy_list.append(from_path) + + for dir in dir_list: + for f in file_list: + # The file is already in one of copied directories + if dir in f: + file_list.remove(f) + continue + if dir.startswith('/'): + dir = dir.partition('/')[2] + absolute_path = os.path.join(location, dir) + if on_windows: + absolute_path = add_unc(absolute_path) + copy_list.append(absolute_path) + + for f in file_list: + if f.startswith('/'): + f = f.partition('/')[2] + absolute_path = os.path.join(location, f) + if on_windows: + absolute_path = add_unc(absolute_path) + copy_list.append(absolute_path) + + return copy_list, errors + + +def about_object_to_list_of_dictionary(abouts): + """ + Convert About objects to a list of dictionaries + """ + serialized = [] + for about in abouts: + # Restore the *_file value to the original value + # The *_file's original_value may be parsed (i.e. split(',)) + # for validation purpose. + about.license_file.value = about.license_file.original_value + about.notice_file.value = about.notice_file.original_value + about.changelog_file.value = about.changelog_file.original_value + about.author_file.value = about.author_file.original_value + + # TODO: this wholeblock should be under sd_dict() + ad = about.as_dict() + + if 'about_file_path' in ad.keys(): + afp = ad['about_file_path'] + afp_parent = posixpath.dirname(afp) + afp_parent = '/' + \ + afp_parent if not afp_parent.startswith( + '/') else afp_parent + + # Update the 'about_resource' field with the relative path + # from the output location + if 'about_resource' in ad.keys(): + about_resource = ad['about_resource'] + for resource in about_resource: + updated_about_resource = posixpath.normpath( + posixpath.join(afp_parent, resource)) + if resource == u'.': + if not updated_about_resource == '/': + updated_about_resource = updated_about_resource + '/' + ad['about_resource'] = dict( + [(updated_about_resource, None)]) + del ad['about_file_path'] + serialized.append(ad) + return serialized + + +def write_output(abouts, location, format): # NOQA + """ + Write a CSV/JSON file at location given a list of About objects. + Return a list of Error objects. + """ + about_dicts = about_object_to_list_of_dictionary(abouts) + if not location == '-': + location = add_unc(location) + if format == 'csv': + save_as_csv(location, about_dicts, get_field_names(abouts)) + elif format == 'json': + save_as_json(location, about_dicts) + else: + save_as_excel(location, about_dicts) + + +def save_as_json(location, about_dicts): + """ + Save the given data as a JSON file or print it to standard output. + """ + data = util.format_about_dict_for_json_output(about_dicts) + if location == '-': + json.dump(data, sys.stdout, indent=2) + else: + with open(location, mode='w') as output_file: + output_file.write(json.dumps(data, indent=2)) + + +def save_as_csv(location, about_dicts, field_names): + """ + Save the given data as a CSV file or print it to standard output. + """ + if location == '-': + writer = csv.DictWriter(sys.stdout, field_names) + writer.writeheader() + csv_formatted_list = util.format_about_dict_output(about_dicts) + for row in csv_formatted_list: + writer.writerow(row) + else: + with open(location, mode='w', encoding='utf-8', newline='', errors='replace') as output_file: + writer = csv.DictWriter(output_file, field_names) + writer.writeheader() + csv_formatted_list = util.format_about_dict_output(about_dicts) + for row in csv_formatted_list: + writer.writerow(row) + + +def save_as_excel(location, about_dicts): + """ + Save the given data as a Excel file. + """ + formatted_list = util.format_about_dict_output(about_dicts) + write_excel(location, formatted_list) + + +def pre_process_and_fetch_license_dict(abouts, from_check=False, api_url=None, api_key=None, scancode=False, reference=None): + """ + Return a dictionary containing the license information (key, name, text, url) + fetched from the ScanCode LicenseDB or DejaCode API. + """ + key_text_dict = {} + captured_license = [] + errors = [] + if api_url: + dje_uri = urlparse(api_url) + domain = '{uri.scheme}://{uri.netloc}/'.format(uri=dje_uri) + lic_urn = urljoin(domain, 'urn/?urn=urn:dje:license:') + url = api_url + else: + url = 'https://scancode-licensedb.aboutcode.org/' + if util.have_network_connection(): + if not valid_api_url(url): + msg = u"URL not reachable. Invalid 'URL. License generation is skipped." + errors.append(Error(ERROR, msg)) + else: + msg = u'Network problem. Please check your Internet connection. License generation is skipped.' + errors.append(Error(ERROR, msg)) + + if errors: + return key_text_dict, errors + + spdx_sclickey_dict = get_spdx_key_and_lic_key_from_licdb() + for about in abouts: + # No need to go through all the about objects if '--api_key' is invalid + auth_error = Error( + ERROR, u"Authorization denied. Invalid '--api_key'. License generation is skipped.") + if auth_error in errors: + break + + if scancode: + lic_exp = '' + lic_list = [] + if about.detected_license_expression.value: + lic_exp = about.detected_license_expression.value + about.license_expression.value = lic_exp + about.license_expression.present = True + + afp = '' + if about.about_file_path: + afp = about.about_file_path + + if not about.license_expression.value and about.spdx_license_expression.value: + lic_exp_value = "" + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + about.spdx_license_expression.value) + if special_char_in_expression or invalid_lic_exp: + if special_char_in_expression: + if afp: + msg = (afp + u": The following character(s) cannot be in the spdx_license_expression: " + + str(special_char_in_expression)) + else: + msg = (u"The following character(s) cannot be in the spdx_license_expression: " + + str(special_char_in_expression)) + else: + if afp: + msg = (afp + u": This spdx_license_expression is invalid: " + + str(invalid_lic_exp)) + else: + msg = (u"This spdx_license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + else: + spdx_lic_exp_segment = about.spdx_license_expression.value.split() + for spdx_lic_key in spdx_lic_exp_segment: + if lic_exp_value: + lic_exp_value = lic_exp_value + " " + convert_spdx_expression_to_lic_expression( + spdx_lic_key, spdx_sclickey_dict) + else: + lic_exp_value = convert_spdx_expression_to_lic_expression( + spdx_lic_key, spdx_sclickey_dict) + if lic_exp_value: + about.license_expression.value = lic_exp_value + about.license_expression.present = True + + lic_exp_list = [] + + if about.declared_license_expression.value: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + about.declared_license_expression.value) + if special_char_in_expression: + if afp: + msg = (afp + u": The following character(s) cannot be in the declared_license_expression: " + + str(special_char_in_expression)) + else: + msg = (u"The following character(s) cannot be in the declared_license_expression: " + + str(special_char_in_expression)) + errors.append(Error(ERROR, msg)) + if invalid_lic_exp: + if afp: + msg = (afp + u": This declared_license_expression is invalid: " + + str(invalid_lic_exp)) + else: + msg = (u"This declared_license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + if lic_list: + lic_exp_list.extend(lic_list) + + if about.other_license_expression.value: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + about.other_license_expression.value) + if special_char_in_expression: + if afp: + msg = (afp + u": The following character(s) cannot be in the other_license_expression: " + + str(special_char_in_expression)) + else: + msg = (u"This declared_license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + if invalid_lic_exp: + if afp: + msg = (afp + u": This other_license_expression is invalid: " + + str(invalid_lic_exp)) + else: + msg = (u"This other_license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + if lic_list: + lic_exp_list.extend(lic_list) + + if about.license_expression.value: + special_char_in_expression, lic_list, invalid_lic_exp = parse_license_expression( + about.license_expression.value) + if special_char_in_expression: + if afp: + msg = (afp + u": The following character(s) cannot be in the license_expression: " + + str(special_char_in_expression)) + else: + msg = (u"The following character(s) cannot be in the license_expression: " + + str(special_char_in_expression)) + errors.append(Error(ERROR, msg)) + if invalid_lic_exp: + if afp: + msg = (afp + u": This license_expression is invalid: " + + str(invalid_lic_exp)) + else: + msg = (u"This license_expression is invalid: " + + str(invalid_lic_exp)) + errors.append(Error(ERROR, msg)) + if lic_list: + lic_exp_list.extend(lic_list) + if not about.license_key.value: + about.license_key.value = lic_list + + if lic_exp_list: + for lic_key in lic_exp_list: + if not lic_key in captured_license: + lic_url = '' + license_name = '' + license_filename = '' + license_text = '' + spdx_license_key = '' + detail_list = [] + captured_license.append(lic_key) + if api_key: + license_data, errs = api.get_license_details_from_api( + url, api_key, lic_key) + # Catch incorrect API URL + if errs: + _, msg = errs[0] + if msg == "Invalid '--api_url'. License generation is skipped.": + errors.extend(errs) + return key_text_dict, errors + for severity, message in errs: + msg = (afp + ": " + message) + errors.append(Error(severity, msg)) + # We don't want to actually get the license information from the + # check utility + if from_check: + continue + if not license_data: + continue + license_name = license_data.get('short_name', '') + license_text = license_data.get('full_text', '') + spdx_license_key = license_data.get( + 'spdx_license_key', '') + license_filename = lic_key + '.LICENSE' + lic_url = lic_urn + lic_key + else: + license_url = url + lic_key + '.json' + license_text_url = url + lic_key + '.LICENSE' + try: + response = head(license_url) + if response.status_code < 400: + json_url_content = get( + license_url).text + # We don't want to actually get the license + # information from the check utility + if from_check: + continue + data = json.loads(json_url_content) + license_name = data['short_name'] + license_text = get( + license_text_url).text + license_filename = data['key'] + '.LICENSE' + lic_url = url + license_filename + spdx_license_key = data['spdx_license_key'] + else: + if afp: + msg = afp + u" : Invalid 'license': " + lic_key + else: + msg = u"Invalid 'license': " + lic_key + errors.append(Error(ERROR, msg)) + continue + except exceptions.RequestException as e: + msg = f"An error occurred while trying to access the URL: {e}" + errors.append(Error(ERROR, msg)) + if not from_check: + detail_list.append(license_name) + detail_list.append(license_filename) + detail_list.append(license_text) + detail_list.append(lic_url) + detail_list.append(spdx_license_key) + key_text_dict[lic_key] = detail_list + return key_text_dict, errors + + +def convert_spdx_expression_to_lic_expression(spdx_key, spdx_lic_dict): + """ + Translate the spdx_license_expression to license_expression and return + errors if spdx_license_key is not matched + """ + value = "" + if spdx_key in spdx_lic_dict: + value = spdx_lic_dict[spdx_key] + else: + if spdx_key.startswith('('): + mod_key = spdx_key.partition('(')[2] + value = '(' + \ + convert_spdx_expression_to_lic_expression( + mod_key, spdx_lic_dict) + elif spdx_key.endswith(')'): + mod_key = spdx_key.rpartition(')')[0] + value = convert_spdx_expression_to_lic_expression( + mod_key, spdx_lic_dict) + ')' + else: + # This can be operator or key that don't have match + value = spdx_key + return value + + +def parse_license_expression(lic_expression): + licensing = Licensing() + lic_list = [] + invalid_lic_exp = '' + special_char = detect_special_char(lic_expression) + if not special_char: + # Parse the license expression and save it into a list + try: + lic_list = licensing.license_keys(lic_expression) + except: + invalid_lic_exp = lic_expression + return special_char, lic_list, invalid_lic_exp + + +def detect_special_char(expression): + not_support_char = [ + '!', '@', '#', '$', '^', '&', '*', '=', '{', '}', + '|', '[', ']', '\\', ':', ';', '<', '>', '?', ',', '/'] + special_character = [] + for char in not_support_char: + if char in expression: + special_character.append(char) + return special_character + + +def valid_api_url(api_url): + try: + response = get(api_url) + # The 403 error code is expected if the api_url is pointing to DJE as no + # API key is provided. The 200 status code represent connection success + # to scancode's LicenseDB. All other exception yield to invalid api_url + if response.status_code == 403 or response.status_code == 200: + return True + else: + return False + except: + return False diff --git a/.venv/lib/python3.11/site-packages/attributecode/templates/default_html.template b/.venv/lib/python3.11/site-packages/attributecode/templates/default_html.template new file mode 100644 index 00000000..c7c31b65 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/templates/default_html.template @@ -0,0 +1,84 @@ + + + + + Open Source Software Information + + + +

OPEN SOURCE SOFTWARE INFORMATION

+

{{ vartext['subtitle'] }}

+
+

Licenses, acknowledgments and required copyright notices for + open source components:

+
+ + + +
+ + {% for about_object in abouts %} +
+

{{ about_object.name.value }} {% if about_object.version.value %}{{ about_object.version.value }}{% endif %}

+ {% if about_object.license_expression.value %} +

This component is licensed under {{ about_object.license_expression.value }}

+ {% endif %} + {% if about_object.copyright.value %} +
Copyright: {{about_object.copyright.value}}
+ {% endif %} + {% if about_object.notice_file.value %} + {% for notice in about_object.notice_file.value %} +
+                    {{ about_object.notice_file.value[notice] }}
+                
+ {% endfor %} + {% endif %} + {% if about_object.license_key.value %} + {% for license_key in about_object.license_key.value %} + {% if license_key in common_licenses %} +

Full text of {{ license_key }} is available at the end of this document.

+ {% else %} + {% for license in licenses_list %} + {% if license_key == license.key %} +

{{ license.key }}

+
{{ license.text | e }}
+ {% endif %} + {% endfor %} + {% endif %} + {% endfor %} + {% else %} + {% if about_object.license_file.value %} + {% for lic_file_name in about_object.license_file.value %} + {% if about_object.license_file.value[lic_file_name] %} +
 {{ about_object.license_file.value[lic_file_name] | e}} 
+ {% endif %} + {% endfor %} + {% endif %} + {% endif %} +
+ {% endfor %} + +
+ +

Common Licenses Used in This Product

+ {% for license in licenses_list %} + {% if license.key in common_licenses %} +

{{ license.key }}

+
 {{ license.text | e }} 
+ {% endif %} + {% endfor %} + +

End

+ + This file was generated with AttributeCode version: {{ tkversion }} on: {{ utcnow }} (UTC) + + + diff --git a/.venv/lib/python3.11/site-packages/attributecode/templates/default_json.template b/.venv/lib/python3.11/site-packages/attributecode/templates/default_json.template new file mode 100644 index 00000000..dc19a5a6 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/templates/default_json.template @@ -0,0 +1,18 @@ +{ + "ossAttribution": { + "title": "Open Source Software Information", + "entries": [ + {% for about_object in abouts %} + { + "name": "{{ about_object.name.value }}"{% if about_object.version.value or about_object.license_expression.value-%},{%- endif %} + {% if about_object.version.value -%} + "version": "{{ about_object.version.value }}"{% if about_object.license_expressio.value-%},{%- endif %} + {%- endif %} + {% if about_object.license_expression.value -%} + "license_expression": "{{ about_object.license_expression.value }}" + {%- endif %} + }{% if not loop.last -%},{%- endif %} + {%- endfor %} + ] + } +} \ No newline at end of file diff --git a/.venv/lib/python3.11/site-packages/attributecode/templates/license_ref.template b/.venv/lib/python3.11/site-packages/attributecode/templates/license_ref.template new file mode 100644 index 00000000..1d0a5395 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/templates/license_ref.template @@ -0,0 +1,69 @@ + + + + + {{ vartext['title'] }} + + +

{{ vartext['title'] }}

+ + +
+
+ {% for license in licenses_list %} +

+ {{ license.name }} + +

+ {% endfor %} + +
+
+ + {% for license in licenses_list %} +
+

{{ license.name }}

+

This product contains the following open source software packages licensed under the terms of the license: {{license.name}}

+ +
+ {%for about_object in abouts %} + {% if loop.first %} + {% if license.url %} +

License Gallery URL: {{license.url}}

+ {% endif %} + {% endif %} + {% if license.key in about_object.license_key.value %} +
  • {{ about_object.name.value }}{% if about_object.version.value %} - Version {{ about_object.version.value }}{% endif %}
  • + {% if about_object.copyright.value %} +
    Copyright: {{about_object.copyright.value}}
    + {% endif %} + {% if about_object.notice_file.value %} + {% for notice in about_object.notice_file.value %} +
    +                                {{ about_object.notice_file.value[notice] }}
    +                            
    + {% endfor %} + {% endif %} + {% endif %} + {% if loop.last %} +
    {{license.text}}
    + {% endif %} + {% endfor %} +
    +
    + {% endfor %} +
    +
    +

    End

    + + diff --git a/.venv/lib/python3.11/site-packages/attributecode/templates/list.csv b/.venv/lib/python3.11/site-packages/attributecode/templates/list.csv new file mode 100644 index 00000000..b576a9b4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/templates/list.csv @@ -0,0 +1,4 @@ +Name,Version,DejaCode License,Homepage +{% for about in abouts %} +"{{about.name}}","{{about.version}}","{{about.license_name}}","{{about.homepage_url}}" +{% endfor %} diff --git a/.venv/lib/python3.11/site-packages/attributecode/templates/scancode_html.template b/.venv/lib/python3.11/site-packages/attributecode/templates/scancode_html.template new file mode 100644 index 00000000..d8582587 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/templates/scancode_html.template @@ -0,0 +1,88 @@ + + + + + Open Source Software Information + + + +

    OPEN SOURCE SOFTWARE INFORMATION

    +

    {{ vartext['subtitle'] }}

    +
    +

    Licenses, acknowledgments and required copyright notices for + open source components:

    +
    + +
    + {% set index = namespace(value=0) %} + {% for about_object in abouts %} + {% set captured = {} %} + {% if about_object.license_key.value %} + {% if not captured[about_object.name.value] %} +

    {{ about_object.name.value }}{% if about_object.version.value %} {{ about_object.version.value }}{% endif %}

    + {% set _ = captured.update({ about_object.name.value: true }) %} + {% set index.value = index.value + 1 %} + {% endif %} + {% endif %} + {% endfor %} +
    + +
    + + {% set index = namespace(value=0) %} + {% for about_object in abouts %} + {% set captured = {} %} + {% if about_object.license_key.value %} + {% if not captured[about_object.name.value] %} +
    +

    {{ about_object.name.value }} {% if about_object.version.value %}{{ about_object.version.value }}{% endif %}

    + {% set _ = captured.update({ about_object.name.value: true }) %} + {% set index.value = index.value + 1 %} + {% endif %} + {% if about_object.copyrights.value %} + {% for copyright in about_object.copyrights.value %} +
     {{ copyright['copyright'] }} 
    + {% endfor %} + {% endif %} + + {% for lic_key_exp in about_object.license_key_expression.value %} +

    This component is licensed under {{ lic_key_exp }}

    + {% endfor %} + + {% for lic_key_exp in about_object.license_key.value %} + {% for lic_key in lic_key_exp %} + {% if lic_key in common_licenses %} +

    Full text of {{ lic_key }} is available at the end of this document.

    + {% else %} + {% for license in licenses_list %} + {% if lic_key == license.key %} +

    {{ license.key }}

    +
     {{ license.text | e }} 
    + {% endif %} + {% endfor %} + {% endif %} + {% endfor %} + {% endfor %} + {% endif %} +
    + {% endfor %} + +
    + +

    Common Licenses Used in This Product

    + {% for license in licenses_list %} + {% if license.key in common_licenses %} +

    {{ license.key }}

    +
     {{ license.text | e }} 
    + {% endif %} + {% endfor %} + +

    End

    + + This file was generated with AttributeCode version: {{ tkversion }} on: {{ utcnow }} (UTC) + + diff --git a/.venv/lib/python3.11/site-packages/attributecode/transform.py b/.venv/lib/python3.11/site-packages/attributecode/transform.py new file mode 100644 index 00000000..46b2412e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/transform.py @@ -0,0 +1,446 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import json +from collections import Counter, OrderedDict +from itertools import zip_longest + +import attr +import openpyxl + +from attributecode import CRITICAL +from attributecode import Error +from attributecode import saneyaml +from attributecode.util import csv +from attributecode.util import replace_tab_with_spaces + + +def transform_csv(location): + """ + Read a CSV file at `location` and convert data into list of dictionaries. + """ + errors = [] + new_data = [] + rows = read_csv_rows(location) + data = iter(rows) + names = next(rows) + field_names = strip_trailing_fields_csv(names) + dupes = check_duplicate_fields(field_names) + + if dupes: + msg = u'Duplicated field name: %(name)s' + for name in dupes: + errors.append(Error(CRITICAL, msg % locals())) + + if not errors: + # Convert to dicts + new_data = [dict(zip_longest(field_names, item)) for item in data] + + return new_data, errors + + +def transform_json(location): + """ + Read a JSON file at `location` and convert data into list of dictionaries. + """ + errors = [] + new_data = [] + items = read_json(location) + data = normalize_dict_data(items) + new_data = strip_trailing_fields_json(data) + + return new_data, errors + + +def transform_excel(location, worksheet=None): + """ + Read a XLSX file at `location` and convert data into list of dictionaries. + """ + errors = [] + new_data = [] + dupes, new_data = read_excel(location, worksheet) + if dupes: + msg = u'Duplicated field name: %(name)s' + for name in dupes: + errors.append(Error(CRITICAL, msg % locals())) + return new_data, errors + + +def strip_trailing_fields_csv(names): + """ + Strip trailing spaces for field names #456 + """ + field_names = [] + for name in names: + field_names.append(name.strip()) + return field_names + + +def strip_trailing_fields_json(items): + """ + Strip trailing spaces for field name #456 + """ + data = [] + for item in items: + od = {} + for field in item: + stripped_field_name = field.strip() + od[stripped_field_name] = item[field] + data.append(od) + return data + + +def normalize_dict_data(data): + """ + Check if the input data from scancode-toolkit and normalize to a normal + dictionary if it is. + Return a list type of normalized dictionary. + """ + try: + # Check if this is a JSON output from scancode-toolkit + if (data["headers"][0]["tool_name"] == "scancode-toolkit"): + # only takes data inside "files" + new_data = data["files"] + except: + new_data = data + if not isinstance(new_data, list): + new_data = [new_data] + return new_data + + +def transform_data(data, transformer): + """ + Read a dictionary and apply transformations using the + `transformer` Transformer. + Return a tuple of: + ([field names...], [transformed ordered dict...], [Error objects..]) + """ + renamed_field_data = transformer.apply_renamings(data) + + if transformer.field_filters: + renamed_field_data = list( + transformer.filter_fields(renamed_field_data)) + + if transformer.exclude_fields: + renamed_field_data = list( + transformer.filter_excluded(renamed_field_data)) + + errors = transformer.check_required_fields(renamed_field_data) + if errors: + return data, errors + return renamed_field_data, errors + + +tranformer_config_help = ''' +A transform configuration file is used to describe which transformations and +validations to apply to a source CSV file. This is a simple text file using YAML +format, using the same format as an .ABOUT file. + +The attributes that can be set in a configuration file are: + +* field_renamings: +An optional map of source CSV or JSON field name to target CSV/JSON new field name that +is used to rename CSV fields. + +For instance with this configuration the fields "Directory/Location" will be +renamed to "about_resource" and "foo" to "bar": + field_renamings: + about_resource : 'Directory/Location' + bar : foo + +The renaming is always applied first before other transforms and checks. All +other field names referenced below are these that exist AFTER the renamings +have been applied to the existing field names. + +* required_fields: +An optional list of required field names that must have a value, beyond the +standard fields names. If a source CSV/JSON does not have such a field or a row is +missing a value for a required field, an error is reported. + +For instance with this configuration an error will be reported if the fields +"name" and "version" are missing or if any row does not have a value set for +these fields: + required_fields: + - name + - version + +* field_filters: +An optional list of field names that should be kept in the transformed CSV/JSON. If +this list is provided, all the fields from the source CSV/JSON that should be kept +in the target CSV/JSON must be listed regardless of either standard or required +fields. If this list is not provided, all source CSV/JSON fields are kept in the +transformed target CSV/JSON. + +For instance with this configuration the target CSV/JSON will only contains the "name" +and "version" fields and no other field: + field_filters: + - name + - version + +* exclude_fields: +An optional list of field names that should be excluded in the transformed CSV/JSON. If +this list is provided, all the fields from the source CSV/JSON that should be excluded +in the target CSV/JSON must be listed. Excluding standard or required fields will cause +an error. If this list is not provided, all source CSV/JSON fields are kept in the +transformed target CSV/JSON. + +For instance with this configuration the target CSV/JSON will not contain the "type" +and "temp" fields: + exclude_fields: + - type + - temp +''' + + +@attr.attributes +class Transformer(object): + __doc__ = tranformer_config_help + + field_renamings = attr.attrib(default=attr.Factory(dict)) + required_fields = attr.attrib(default=attr.Factory(list)) + field_filters = attr.attrib(default=attr.Factory(list)) + exclude_fields = attr.attrib(default=attr.Factory(list)) + + # a list of all the standard fields from AboutCode toolkit + standard_fields = attr.attrib(default=attr.Factory(list), init=False) + # a list of the subset of standard fields that are essential and MUST be + # present for AboutCode toolkit to work + essential_fields = attr.attrib(default=attr.Factory(list), init=False) + + # called by attr after the __init__() + def __attrs_post_init__(self, *args, **kwargs): + from attributecode.model import About + about = About() + self.essential_fields = list(about.required_fields) + self.standard_fields = [f.name for f in about.all_fields()] + + @classmethod + def default(cls): + """ + Return a default Transformer with built-in transforms. + """ + return cls( + field_renamings={}, + required_fields=[], + field_filters=[], + exclude_fields=[], + ) + + @classmethod + def from_file(cls, location): + """ + Load and return a Transformer instance from a YAML configuration file at + `location`. + """ + with open(location, encoding='utf-8', errors='replace') as conf: + data = saneyaml.load(replace_tab_with_spaces(conf.read())) + return cls( + field_renamings=data.get('field_renamings', {}), + required_fields=data.get('required_fields', []), + field_filters=data.get('field_filters', []), + exclude_fields=data.get('exclude_fields', []), + ) + + def check_required_fields(self, data): + """ + Return a list of Error for a `data` list of ordered dict where a + dict is missing a value for a required field name. + """ + errors = [] + required = set(self.essential_fields + self.required_fields) + if not required: + return [] + + for rn, item in enumerate(data): + missings = [rk for rk in required if not item.get(rk)] + if not missings: + continue + + missings = ', '.join(missings) + msg = 'Row {rn} is missing required values for fields: {missings}' + errors.append(Error(CRITICAL, msg.format(**locals()))) + + return errors + + def apply_renamings(self, data): + """ + Return a tranformed list of `field_names` where fields are renamed + based on this Transformer configuration. + """ + renamings = self.field_renamings + renamed_to_list = list(renamings.keys()) + renamed_from_list = list(renamings.values()) + if not renamings: + return data + if isinstance(data, dict): + renamed_obj = {} + for key, value in data.items(): + if key in renamed_from_list: + for idx, renamed_from_key in enumerate(renamed_from_list): + if key == renamed_from_key: + renamed_key = renamed_to_list[idx] + renamed_obj[renamed_key] = self.apply_renamings( + value) + else: + renamed_obj[key] = self.apply_renamings(value) + return renamed_obj + elif isinstance(data, list): + return [self.apply_renamings(item) for item in data] + else: + return data + + """ + def clean_fields(self, field_names): + + Apply standard cleanups to a list of fields and return these. + + if not field_names: + return field_names + return [c.strip().lower() for c in field_names] + """ + + def filter_fields(self, data): + """ + Yield transformed dicts from a `data` list of dicts keeping only + fields with a name in the `field_filters`of this Transformer. + Return the data unchanged if no `field_filters` exists. + """ + # field_filters = set(self.clean_fields(self.field_filters)) + field_filters = set(self.field_filters) + for entry in data: + yield {k: v for k, v in entry.items() if k in field_filters} + + def filter_excluded(self, data): + """ + Yield transformed dicts from a `data` list of dicts excluding + fields with names in the `exclude_fields`of this Transformer. + Return the data unchanged if no `exclude_fields` exists. + """ + # exclude_fields = set(self.clean_fields(self.exclude_fields)) + exclude_fields = set(self.exclude_fields) + filtered_list = [] + for entry in data: + result = {} + for k, v in entry.items(): + if type(v) == list: + result[k] = self.filter_excluded(v) + elif k not in exclude_fields: + result[k] = v + filtered_list.append(result) + # yield result + # yield {k: v for k, v in entry.items() if k not in exclude_fields} + return filtered_list + + +def check_duplicate_fields(field_names): + """ + Check that there are no duplicate in the `field_names` list of field name + strings, ignoring case. Return a list of unique duplicated field names. + """ + counted = Counter(c.lower() for c in field_names) + return [field for field, count in sorted(counted.items()) if count > 1] + + +def read_csv_rows(location): + """ + Yield rows (as a list of values) from a CSV file at `location`. + """ + with open(location, encoding='utf-8', errors='replace') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + yield row + + +def read_json(location): + """ + Yield rows (as a list of values) from a CSV file at `location`. + """ + with open(location, encoding='utf-8', errors='replace') as jsonfile: + return json.load(jsonfile) + + +def write_csv(location, data): + """ + Write a CSV file at `location` with the `data` which is a list of ordered dicts. + """ + field_names = list(data[0].keys()) + with open(location, 'w', encoding='utf-8', newline='\n', errors='replace') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=field_names) + writer.writeheader() + writer.writerows(data) + + +def write_json(location, data): + """ + Write a JSON file at `location` the `data` list of ordered dicts. + """ + with open(location, 'w') as jsonfile: + json.dump(data, jsonfile, indent=3) + + +def read_excel(location, worksheet=None): + """ + Read XLSX at `location`, return a list of ordered dictionaries, one + for each row. + """ + results = [] + errors = [] + input_bom = openpyxl.load_workbook(location) + if worksheet: + sheet_obj = input_bom[worksheet] + else: + sheet_obj = input_bom.active + max_col = sheet_obj.max_column + + index = 1 + col_keys = [] + mapping_dict = {} + while index <= max_col: + value = sheet_obj.cell(row=1, column=index).value + if value in col_keys: + msg = 'Duplicated column name, ' + str(value) + ', detected.' + errors.append(Error(CRITICAL, msg)) + return errors, results + if value in mapping_dict: + value = mapping_dict[value] + col_keys.append(value) + index = index + 1 + + for row in sheet_obj.iter_rows(min_row=2, values_only=True): + row_dict = OrderedDict() + index = 0 + while index < max_col: + value = row[index] + if value: + row_dict[col_keys[index]] = value + else: + row_dict[col_keys[index]] = '' + index = index + 1 + results.append(row_dict) + return errors, results + + +def write_excel(location, data): + wb = openpyxl.Workbook() + ws = wb.active + + # Get the header + headers = list(data[0].keys()) + ws.append(headers) + + for elements in data: + ws.append([elements.get(h) for h in headers]) + + wb.save(location) diff --git a/.venv/lib/python3.11/site-packages/attributecode/util.py b/.venv/lib/python3.11/site-packages/attributecode/util.py new file mode 100644 index 00000000..5f398d7d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attributecode/util.py @@ -0,0 +1,873 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- +# ============================================================================ +# Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +from collections import OrderedDict + +import codecs +import csv +import json +import ntpath +import openpyxl +import os +import posixpath +import re +import shutil +import string +import sys +from distutils.dir_util import copy_tree +from itertools import zip_longest + +from attributecode import CRITICAL +from attributecode import WARNING +from attributecode import Error + +on_windows = "win32" in sys.platform + +# boolean field name +boolean_fields = [ + "redistribute", + "attribute", + "track_change", + "modified", + "internal_use_only", +] +file_fields = ["about_resource", "notice_file", "changelog_file", "author_file"] + + +def to_posix(path): + """ + Return a path using the posix path separator given a path that may contain + posix or windows separators, converting "\\" to "/". NB: this path will + still be valid in the windows explorer (except for a UNC or share name). It + will be a valid path everywhere in Python. It will not be valid for windows + command line operations. + """ + return path.replace(ntpath.sep, posixpath.sep) + + +UNC_PREFIX = "\\\\?\\" +UNC_PREFIX_POSIX = to_posix(UNC_PREFIX) +UNC_PREFIXES = ( + UNC_PREFIX_POSIX, + UNC_PREFIX, +) + +valid_file_chars = "_-.+()~[]{}@%!$," +invalid_file_chars = string.punctuation.translate( + str.maketrans("", "", valid_file_chars) +) + + +def invalid_chars(path): + """ + Return a list of invalid characters in the file name of `path`. + """ + path = to_posix(path) + rname = resource_name(path) + name = rname.lower() + + return [c for c in name if c in invalid_file_chars] + + +def check_file_names(paths): + """ + Given a sequence of file paths, check that file names are valid and that + there are no case-insensitive duplicates in any given directories. + Return a list of errors. + + From spec: + The case of a file name is not significant. On case-sensitive file + systems (such as Linux), a tool must raise an error if two ABOUT files + stored in the same directory have the same lowercase file name. + """ + # FIXME: this should be a defaultdicts that accumulates all duplicated paths + seen = {} + errors = [] + for orig_path in paths: + path = orig_path + invalid = invalid_chars(path) + if invalid: + invalid = "".join(invalid) + msg = "Invalid characters %(invalid)r in file name at: %(path)r" % locals() + errors.append(Error(CRITICAL, msg)) + + path = to_posix(orig_path) + name = resource_name(path).lower() + parent = posixpath.dirname(path) + path = posixpath.join(parent, name) + path = posixpath.normpath(path) + path = posixpath.abspath(path) + existing = seen.get(path) + if existing: + msg = ( + "Duplicate files: %(orig_path)r and %(existing)r " + "have the same case-insensitive file name" % locals() + ) + errors.append(Error(CRITICAL, msg)) + else: + seen[path] = orig_path + return errors + + +def wrap_boolean_value(context): + updated_context = "" + for line in context.splitlines(): + """ + wrap the boolean value in quote + """ + key = line.partition(":")[0] + value = line.partition(":")[2].strip() + value = '"' + value + '"' + if key in boolean_fields and not value == "": + updated_context += key + ": " + value + "\n" + else: + updated_context += line + "\n" + return updated_context + + +def replace_tab_with_spaces(context): + updated_context = "" + for line in context.splitlines(): + """ + Replace tab with 4 spaces + """ + updated_context += line.replace("\t", " ") + "\n" + return updated_context + + +# TODO: rename to normalize_path +def get_absolute(location): + """ + Return an absolute normalized location. + """ + location = os.path.expanduser(location) + location = os.path.expandvars(location) + location = os.path.normpath(location) + location = os.path.abspath(location) + return location + + +def get_locations(location): + """ + Return a list of locations of files given the `location` of a + a file or a directory tree containing ABOUT files. + File locations are normalized using posix path separators. + """ + location = add_unc(location) + location = get_absolute(location) + assert os.path.exists(location) + + if os.path.isfile(location): + yield location + else: + for base_dir, _, files in os.walk(location): + for name in files: + bd = to_posix(base_dir) + yield posixpath.join(bd, name) + + +def get_about_locations(location, exclude=None): + """ + Return a list of locations of ABOUT files given the `location` of a + a file or a directory tree containing ABOUT files. + File locations are normalized using posix path separators. + """ + pattern_characters_list = ["*", "?", "[", "!"] + import fnmatch + + for loc in get_locations(location): + exclude_match = False + if exclude: + for item in exclude: + is_pattern = False + for character in pattern_characters_list: + if character in item: + is_pattern = True + break + exclude_path = posixpath.join(location, item) + normalized_excluded_path = posixpath.normpath( + add_unc(exclude_path).replace("\\", "/") + ) + # Since 'normpath' removes the trailing '/', it is necessary + # to append the '/' back for proper matching. + if not is_pattern and item.endswith("/"): + normalized_excluded_path += "/" + if is_pattern: + if fnmatch.fnmatch(loc, normalized_excluded_path): + exclude_match = True + break + else: + if normalized_excluded_path in loc: + exclude_match = True + break + if not exclude_match: + if is_about_file(loc): + yield loc + + +def norm(p): + """ + Normalize the path + """ + if p.startswith(UNC_PREFIX) or p.startswith(to_posix(UNC_PREFIX)): + p = p.strip(UNC_PREFIX).strip(to_posix(UNC_PREFIX)) + p = to_posix(p) + p = p.strip(posixpath.sep) + p = posixpath.normpath(p) + return p + + +def get_spdx_key_and_lic_key_from_licdb(): + """ + Return a dictionary list that fetch all licenses from licenseDB. The + "spdx_license_key" will be the key of the dictionary and the "license_key" + will be the value of the directionary + """ + import requests + + lic_dict = dict() + + # URL of the license index + url = "https://scancode-licensedb.aboutcode.org/index.json" + + """ + Sample of one of the license in the index.json + { + "license_key": "bsd-new", + "category": "Permissive", + "spdx_license_key": "BSD-3-Clause", + "other_spdx_license_keys": [ + "LicenseRef-scancode-libzip" + ], + "is_exception": false, + "is_deprecated": false, + "json": "bsd-new.json", + "yaml": "bsd-new.yml", + "html": "bsd-new.html", + "license": "bsd-new.LICENSE" + }, + """ + response = requests.get(url) + # Check if the request was successful (status code 200) + if response.status_code == 200: + # Retrieve the JSON data from the response + licenses_index = response.json() + + for license in licenses_index: + lic_dict[license["spdx_license_key"]] = license["license_key"] + if license["other_spdx_license_keys"]: + for other_spdx in license["other_spdx_license_keys"]: + lic_dict[other_spdx] = license["license_key"] + + return lic_dict + + +def get_relative_path(base_loc, full_loc): + """ + Return a posix path for a given full location relative to a base location. + The first segment of the different between full_loc and base_loc will become + the first segment of the returned path. + """ + base = norm(base_loc) + path = norm(full_loc) + + assert path.startswith(base), ( + "Cannot compute relative path: %(path)r does not start with %(base)r" % locals() + ) + base_name = resource_name(base) + no_dir = base == base_name + same_loc = base == path + if same_loc: + # this is the case of a single file or single dir + if no_dir: + # we have no dir: the full path is the same as the resource name + relative = base_name + else: + # we have at least one dir + parent_dir = posixpath.dirname(base) + parent_dir = resource_name(parent_dir) + relative = posixpath.join(parent_dir, base_name) + else: + relative = path[len(base) + 1 :] + # We don't want to keep the first segment of the root of the returned path. + # See https://github.com/nexB/attributecode/issues/276 + # relative = posixpath.join(base_name, relative) + return relative + + +def to_native(path): + """ + Return a path using the current OS path separator given a path that may + contain posix or windows separators, converting "/" to "\\" on windows + and "\\" to "/" on posix OSes. + """ + path = path.replace(ntpath.sep, os.path.sep) + path = path.replace(posixpath.sep, os.path.sep) + return path + + +def is_about_file(path): + """ + Return True if the path represents a valid ABOUT file name. + """ + if path: + path = path.lower() + return path.endswith(".about") and path != ".about" + + +def resource_name(path): + """ + Return the file or directory name from a path. + """ + path = path.strip() + path = to_posix(path) + path = path.rstrip(posixpath.sep) + _left, right = posixpath.split(path) + return right.strip() + + +def load_csv(location): + """ + Read CSV at `location`, return a list of ordered dictionaries, one + for each row. + """ + results = [] + with open(location, mode="r", encoding="utf-8-sig", errors="replace") as csvfile: + for row in csv.DictReader(csvfile): + # convert all the column keys to lower case + updated_row = {key.lower().strip(): value for key, value in row.items()} + results.append(updated_row) + return results + + +def load_json(location): + """ + Read JSON file at `location` and return a list of ordered dicts, one for + each entry. + """ + with open(location) as json_file: + results = json.load(json_file) + + if not isinstance(results, list): + results = [results] + + return results + + +# FIXME: rename to is_online: BUT do we really need this at all???? +# This is needed to check for the network connection when user wants to fetch +# the licenses from DJE/LicenseDB +def have_network_connection(): + """ + Return True if an HTTP connection to some public web site is possible. + """ + import requests + + url = "https://scancode-licensedb.aboutcode.org/" + + response = requests.get(url) + if response.status_code == 200: + return True + else: + return False + + +def extract_zip(location): + """ + Extract a zip file at location in a temp directory and return the temporary + directory where the archive was extracted. + """ + import zipfile + import tempfile + + if not zipfile.is_zipfile(location): + raise Exception("Incorrect zip file %(location)r" % locals()) + + archive_base_name = os.path.basename(location).replace(".zip", "") + base_dir = tempfile.mkdtemp(prefix="aboutcode-toolkit-extract-") + target_dir = os.path.join(base_dir, archive_base_name) + target_dir = add_unc(target_dir) + os.makedirs(target_dir) + + if target_dir.endswith((ntpath.sep, posixpath.sep)): + target_dir = target_dir[:-1] + + with zipfile.ZipFile(location) as zipf: + for info in zipf.infolist(): + name = info.filename + content = zipf.read(name) + target = os.path.join(target_dir, name) + is_dir = target.endswith((ntpath.sep, posixpath.sep)) + if is_dir: + target = target[:-1] + parent = os.path.dirname(target) + if on_windows: + target = target.replace(posixpath.sep, ntpath.sep) + parent = parent.replace(posixpath.sep, ntpath.sep) + if not os.path.exists(parent): + os.makedirs(add_unc(parent)) + if not content and is_dir: + if not os.path.exists(target): + os.makedirs(add_unc(target)) + if not os.path.exists(target): + with open(target, "wb") as f: + f.write(content) + return target_dir + + +def add_unc(location): + """ + Convert a `location` to an absolute Window UNC path to support long paths on + Windows. Return the location unchanged if not on Windows. See + https://msdn.microsoft.com/en-us/library/aa365247.aspx + """ + if on_windows and not location.startswith(UNC_PREFIX): + if location.startswith(UNC_PREFIX_POSIX): + return UNC_PREFIX + os.path.abspath(location.strip(UNC_PREFIX_POSIX)) + return UNC_PREFIX + os.path.abspath(location) + return location + + +# FIXME: add docstring +def copy_license_notice_files(fields, base_dir, reference_dir, afp): + """ + Given a list of (key, value) `fields` tuples and a `base_dir` where ABOUT + files and their companion LICENSe are store, and an extra `reference_dir` + where reference license an notice files are stored and the `afp` + about_file_path value, this function will copy to the base_dir the + license_file or notice_file if found in the reference_dir + """ + errors = [] + copy_file_name = "" + for key, value in fields: + if key == "license_file" or key == "notice_file": + if value: + # This is to handle multiple license_file value in CSV format + # The following code will construct a list to contain the + # license file(s) that need to be copied. + # Note that *ONLY* license_file field allows \n. Others file + # fields that have \n will prompts error at validation stage + file_list = [] + if "\n" in value: + f_list = value.split("\n") + else: + if not isinstance(value, list): + f_list = [value] + else: + f_list = value + # The following code is to adopt the approach from #404 + # to use comma for multiple files which refer the same license + for item in f_list: + if "," in item: + item_list = item.split(",") + for i in item_list: + file_list.append(i.strip()) + else: + file_list.append(item) + else: + continue + + for copy_file_name in file_list: + from_lic_path = posixpath.join(to_posix(reference_dir), copy_file_name) + about_file_dir = os.path.dirname(to_posix(afp)).lstrip("/") + to_lic_path = posixpath.join(to_posix(base_dir), about_file_dir) + if not os.path.exists(posixpath.join(to_lic_path, copy_file_name)): + err = copy_file(from_lic_path, to_lic_path) + if err: + errors.append(err) + return errors + + +def copy_file(from_path, to_path): + error = "" + # Return if the from_path is empty or None. + if not from_path: + return + + if on_windows: + if not from_path.startswith(UNC_PREFIXES): + from_path = add_unc(from_path) + if not to_path.startswith(UNC_PREFIXES): + to_path = add_unc(to_path) + + # Strip the white spaces + from_path = from_path.strip() + to_path = to_path.strip() + # Errors will be captured when doing the validation + if not os.path.exists(from_path): + return "" + + if not posixpath.exists(to_path): + os.makedirs(to_path) + try: + if os.path.isdir(from_path): + # Copy the whole directory structure + if from_path.endswith("/"): + from_path = from_path.rpartition("/")[0] + folder_name = os.path.basename(from_path) + to_path = os.path.join(to_path, folder_name) + if os.path.exists(to_path): + msg = to_path + " is already existed and is replaced by " + from_path + error = Error(WARNING, msg) + copy_tree(from_path, to_path) + else: + file_name = os.path.basename(from_path) + to_file_path = os.path.join(to_path, file_name) + if os.path.exists(to_file_path): + msg = ( + to_file_path + " is already existed and is replaced by " + from_path + ) + error = Error(WARNING, msg) + shutil.copy2(from_path, to_path) + return error + except Exception as e: + msg = "Cannot copy file at %(from_path)r." % locals() + error = Error(CRITICAL, msg) + return error + + +def ungroup_licenses_from_sctk(value): + # Return a list of dictionary with lic_key and score + # extracted from SCTK scan + detected_license_list = [] + for detected_license in value: + for lic in detected_license["matches"]: + lic_exp = lic["license_expression"] + score = lic["score"] + detected_license_list.append({"lic_exp": lic_exp, "score": score}) + return detected_license_list + + +# FIXME: we should use a license object instead + + +def ungroup_licenses(licenses): + """ + Ungroup multiple licenses information + """ + lic_key = [] + lic_name = [] + lic_file = [] + lic_url = [] + spdx_lic_key = [] + lic_score = [] + lic_matched_text = [] + for lic in licenses: + if "key" in lic: + lic_key.append(lic["key"]) + if "name" in lic: + lic_name.append(lic["name"]) + if "file" in lic: + lic_file.append(lic["file"]) + if "url" in lic: + lic_url.append(lic["url"]) + if "spdx_license_key" in lic: + spdx_lic_key.append(lic["spdx_license_key"]) + if "score" in lic: + lic_score.append(lic["score"]) + if "matched_text" in lic: + lic_matched_text.append(lic["matched_text"]) + return ( + lic_key, + lic_name, + lic_file, + lic_url, + spdx_lic_key, + lic_score, + lic_matched_text, + ) + + +# FIXME: add docstring +def format_about_dict_output(about_dictionary_list): + formatted_list = [] + for element in about_dictionary_list: + row_list = dict() + for key in element: + if element[key]: + if isinstance(element[key], list): + row_list[key] = "\n".join((element[key])) + elif key == "about_resource": + row_list[key] = "\n".join((element[key].keys())) + else: + row_list[key] = element[key] + formatted_list.append(row_list) + return formatted_list + + +# FIXME: add docstring +def format_about_dict_for_json_output(about_dictionary_list): + licenses = ["license_key", "license_name", "license_file", "license_url"] + json_formatted_list = [] + for element in about_dictionary_list: + row_list = dict() + # FIXME: aboid using parallel list... use an object instead + license_key = [] + license_name = [] + license_file = [] + license_url = [] + + for key in element: + if element[key]: + # The 'about_resource' is an ordered dict + if key == "about_resource": + row_list[key] = list(element[key].keys())[0] + elif key in licenses: + if key == "license_key": + license_key = element[key] + elif key == "license_name": + license_name = element[key] + elif key == "license_file": + license_file = element[key] + elif key == "license_url": + license_url = element[key] + else: + row_list[key] = element[key] + + # Group the same license information in a list + license_group = list( + zip_longest(license_key, license_name, license_file, license_url) + ) + if license_group: + licenses_list = [] + for lic_group in license_group: + lic_dict = dict() + if lic_group[0]: + lic_dict["key"] = lic_group[0] + if lic_group[1]: + lic_dict["name"] = lic_group[1] + if lic_group[2]: + lic_dict["file"] = lic_group[2] + if lic_group[3]: + lic_dict["url"] = lic_group[3] + licenses_list.append(lic_dict) + row_list["licenses"] = licenses_list + json_formatted_list.append(row_list) + return json_formatted_list + + +def unique(sequence): + """ + Return a list of unique items found in sequence. Preserve the original + sequence order. + For example: + >>> unique([1, 5, 3, 5]) + [1, 5, 3] + """ + deduped = [] + for item in sequence: + if item not in deduped: + deduped.append(item) + return deduped + + +def filter_errors(errors, minimum_severity=WARNING): + """ + Return a list of unique `errors` Error object filtering errors that have a + severity below `minimum_severity`. + """ + return [e for e in errors if e.severity >= minimum_severity] + + +def create_dir(location): + """ + Create directory or directory tree at location, ensuring it is readable + and writeable. + """ + import stat + + if not os.path.exists(location): + os.makedirs(location) + os.chmod(location, stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH) + + +def get_temp_dir(sub_dir_path=None): + """ + Create a unique new temporary directory location. Create directories + identified by sub_dir_path if provided in this temporary directory. + Return the location for this unique directory joined with the + sub_dir_path if any. + """ + new_temp_dir = build_temp_dir() + + if sub_dir_path: + # create a sub directory hierarchy if requested + new_temp_dir = os.path.join(new_temp_dir, sub_dir_path) + create_dir(new_temp_dir) + return new_temp_dir + + +def build_temp_dir(prefix="attributecode-"): + """ + Create and return a new unique empty directory created in base_dir. + """ + import tempfile + + location = tempfile.mkdtemp(prefix=prefix) + create_dir(location) + return location + + +def get_file_text(file_name, reference): + """ + Return the file content from the license_file/notice_file field from the + given reference directory. + """ + error = "" + text = "" + file_path = os.path.join(reference, file_name) + if not os.path.exists(file_path): + msg = "The file " + file_path + " does not exist" + error = Error(CRITICAL, msg) + else: + with codecs.open( + file_path, "rb", encoding="utf-8-sig", errors="replace" + ) as txt: + # with io.open(file_path, encoding='utf-8') as txt: + text = txt.read() + return error, text + + +def convert_object_to_dict(about): + """ + Convert the list of field object + [Field(name='name', value=''), Field(name='version', value='')] + to a dictionary + """ + about_dict = {} + # Convert all the supported fields into a dictionary + fields_dict = getattr(about, "fields") + custom_fields_dict = getattr(about, "custom_fields") + supported_dict = {**fields_dict, **custom_fields_dict} + for field in supported_dict: + key = supported_dict[field].name + value = supported_dict[field].value + about_dict[key] = value + return about_dict + + +def load_scancode_json(location): + """ + Read the scancode JSON file at `location` and return a list of dictionaries. + """ + updated_results = [] + + with open(location) as json_file: + results = json.load(json_file) + results = results["files"] + # Rename the "path" to "about_resource" and update "name" from path value + for item in results: + updated_dict = {} + for key in item: + if key == "path": + updated_dict["about_resource"] = item[key] + updated_dict["name"] = os.path.basename(item[key]) + else: + updated_dict[key] = item[key] + updated_results.append(updated_dict) + return updated_results + + +def load_excel(location, worksheet=None): + """ + Read XLSX at `location`, return a list of ordered dictionaries, one + for each row. + """ + results = [] + errors = [] + import warnings + + # This is to prevent showing the: warn("Workbook contains no default style, apply openpyxl's default") + with warnings.catch_warnings(record=True): + input_bom = openpyxl.load_workbook(location) + sheetnames = input_bom.sheetnames + if worksheet: + if worksheet not in sheetnames: + import sys + + print("The input worksheet name does not exist. Exiting.") + sys.exit(1) + sheet_obj = input_bom[worksheet] + else: + sheet_obj = input_bom.active + print("Working on the " + sheet_obj.title + " worksheet.") + max_col = sheet_obj.max_column + + index = 1 + col_keys = [] + mapping_dict = {} + + while index <= max_col: + value = sheet_obj.cell(row=1, column=index).value + if value in col_keys: + msg = "Duplicated column name, " + str(value) + ", detected." + errors.append(Error(CRITICAL, msg)) + return errors, results + if value in mapping_dict: + value = mapping_dict[value] + col_keys.append(value) + index = index + 1 + + for row in sheet_obj.iter_rows(min_row=2, values_only=True): + row_dict = OrderedDict() + index = 0 + while index < max_col: + value = row[index] + if value: + row_dict[col_keys[index]] = value + else: + row_dict[col_keys[index]] = "" + index = index + 1 + results.append(row_dict) + return errors, results + + +def write_licenses(lic_dict, location): + import io + + loc = to_posix(location) + errors = [] + + if not posixpath.exists(loc): + os.makedirs(add_unc(loc)) + try: + for lic in lic_dict: + output_location = posixpath.join(loc, lic) + with open(output_location, "w", encoding="utf-8", errors="replace") as out: + out.write(lic_dict[lic]) + except Exception as e: + msg = str(e) + errors.append(Error(CRITICAL, msg)) + return errors + + +def strip_inventory_value(inventory): + """ + The inventory is a list of dictionaries. This function will strip the value + of the dictionary and return the stripped dictionary to a list + """ + stripped_inventory = [] + for component in inventory: + comp_dict = {} + for key in component: + comp_dict[key] = str(component[key]).strip() + stripped_inventory.append(comp_dict) + return stripped_inventory + + +""" +Return True if a string s name is safe to use as an attribute name. +""" +is_valid_name = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$").match diff --git a/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/METADATA new file mode 100644 index 00000000..51128bb9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/METADATA @@ -0,0 +1,235 @@ +Metadata-Version: 2.4 +Name: attrs +Version: 25.4.0 +Summary: Classes Without Boilerplate +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: GitHub, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Author-email: Hynek Schlawack +License-Expression: MIT +License-File: LICENSE +Keywords: attribute,boilerplate,class +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown + +

    + + attrs + +

    + + +*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). +Trusted by NASA for [Mars missions since 2020](https://github.com/readme/featured/nasa-ingenuity-helicopter)! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + + +## Sponsors + +*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). +Especially those generously supporting us at the *The Organization* tier and higher: + + + +

    + + + + + + + + + +

    + + + +

    + Please consider joining them to help make attrs’s maintenance more sustainable! +

    + + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation! + + +### Hate Type Annotations!? + +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types: + +```python +from attrs import define, field + +@define +class SomeClass: + a_number = field(default=42) + list_of_numbers = field(factory=list) +``` + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice. + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek). + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +## Release Information + +### Backwards-incompatible Changes + +- Class-level `kw_only=True` behavior is now consistent with `dataclasses`. + + Previously, a class that sets `kw_only=True` makes all attributes keyword-only, including those from base classes. + If an attribute sets `kw_only=False`, that setting is ignored, and it is still made keyword-only. + + Now, only the attributes defined in that class that doesn't explicitly set `kw_only=False` are made keyword-only. + + This shouldn't be a problem for most users, unless you have a pattern like this: + + ```python + @attrs.define(kw_only=True) + class Base: + a: int + b: int = attrs.field(default=1, kw_only=False) + + @attrs.define + class Subclass(Base): + c: int + ``` + + Here, we have a `kw_only=True` *attrs* class (`Base`) with an attribute that sets `kw_only=False` and has a default (`Base.b`), and then create a subclass (`Subclass`) with required arguments (`Subclass.c`). + Previously this would work, since it would make `Base.b` keyword-only, but now this fails since `Base.b` is positional, and we have a required positional argument (`Subclass.c`) following another argument with defaults. + [#1457](https://github.com/python-attrs/attrs/issues/1457) + + +### Changes + +- Values passed to the `__init__()` method of `attrs` classes are now correctly passed to `__attrs_pre_init__()` instead of their default values (in cases where *kw_only* was not specified). + [#1427](https://github.com/python-attrs/attrs/issues/1427) +- Added support for Python 3.14 and [PEP 749](https://peps.python.org/pep-0749/). + [#1446](https://github.com/python-attrs/attrs/issues/1446), + [#1451](https://github.com/python-attrs/attrs/issues/1451) +- `attrs.validators.deep_mapping()` now allows to leave out either *key_validator* xor *value_validator*. + [#1448](https://github.com/python-attrs/attrs/issues/1448) +- `attrs.validators.deep_iterator()` and `attrs.validators.deep_mapping()` now accept lists and tuples for all validators and wrap them into a `attrs.validators.and_()`. + [#1449](https://github.com/python-attrs/attrs/issues/1449) +- Added a new **experimental** way to inspect classes: + + `attrs.inspect(cls)` returns the _effective_ class-wide parameters that were used by *attrs* to construct the class. + + The returned class is the same data structure that *attrs* uses internally to decide how to construct the final class. + [#1454](https://github.com/python-attrs/attrs/issues/1454) +- Fixed annotations for `attrs.field(converter=...)`. + Previously, a `tuple` of converters was only accepted if it had exactly one element. + [#1461](https://github.com/python-attrs/attrs/issues/1461) +- The performance of `attrs.asdict()` has been improved by 45–260%. + [#1463](https://github.com/python-attrs/attrs/issues/1463) +- The performance of `attrs.astuple()` has been improved by 49–270%. + [#1469](https://github.com/python-attrs/attrs/issues/1469) +- The type annotation for `attrs.validators.or_()` now allows for different types of validators. + + This was only an issue on Pyright. + [#1474](https://github.com/python-attrs/attrs/issues/1474) + + + +--- + +[Full changelog →](https://www.attrs.org/en/stable/changelog.html) diff --git a/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/RECORD new file mode 100644 index 00000000..b4287f68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/RECORD @@ -0,0 +1,55 @@ +attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 +attr/__init__.pyi,sha256=IZkzIjvtbRqDWGkDBIF9dd12FgDa379JYq3GHnVOvFQ,11309 +attr/__pycache__/__init__.cpython-311.pyc,, +attr/__pycache__/_cmp.cpython-311.pyc,, +attr/__pycache__/_compat.cpython-311.pyc,, +attr/__pycache__/_config.cpython-311.pyc,, +attr/__pycache__/_funcs.cpython-311.pyc,, +attr/__pycache__/_make.cpython-311.pyc,, +attr/__pycache__/_next_gen.cpython-311.pyc,, +attr/__pycache__/_version_info.cpython-311.pyc,, +attr/__pycache__/converters.cpython-311.pyc,, +attr/__pycache__/exceptions.cpython-311.pyc,, +attr/__pycache__/filters.cpython-311.pyc,, +attr/__pycache__/setters.cpython-311.pyc,, +attr/__pycache__/validators.cpython-311.pyc,, +attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 +attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 +attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 +attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 +attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 +attr/_make.py,sha256=NRJDGS8syg2h3YNflVNoK2FwR3CpdSZxx8M6lacwljA,104141 +attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 +attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 +attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 +attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 +attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 +attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 +attr/validators.py,sha256=1BnYGTuYvSucGEI4ju-RPNJteVzG0ZlfWpJiWoSFHQ8,21458 +attr/validators.pyi,sha256=ftmW3m4KJ3pQcIXAj-BejT7BY4ZfqrC1G-5W7XvoPds,4082 +attrs-25.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-25.4.0.dist-info/METADATA,sha256=2Rerxj7agcMRxiwdkt6lC2guqHAmkGKCH13nWWK7ZoQ,10473 +attrs-25.4.0.dist-info/RECORD,, +attrs-25.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +attrs-25.4.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 +attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 +attrs/__pycache__/__init__.cpython-311.pyc,, +attrs/__pycache__/converters.cpython-311.pyc,, +attrs/__pycache__/exceptions.cpython-311.pyc,, +attrs/__pycache__/filters.cpython-311.pyc,, +attrs/__pycache__/setters.cpython-311.pyc,, +attrs/__pycache__/validators.cpython-311.pyc,, +attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/WHEEL new file mode 100644 index 00000000..12228d41 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE new file mode 100644 index 00000000..2bd6453d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack and the attrs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.11/site-packages/attrs/__init__.py b/.venv/lib/python3.11/site-packages/attrs/__init__.py new file mode 100644 index 00000000..dc1ce4b9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/__init__.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + AttrsInstance, + Converter, + Factory, + NothingType, + _make_getattr, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._make import ClassProps +from attr._next_gen import asdict, astuple, inspect + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "ClassProps", + "Converter", + "Factory", + "NothingType", + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "has", + "inspect", + "make_class", + "mutable", + "resolve_types", + "setters", + "validate", + "validators", +] + +__getattr__ = _make_getattr(__name__) diff --git a/.venv/lib/python3.11/site-packages/attrs/__init__.pyi b/.venv/lib/python3.11/site-packages/attrs/__init__.pyi new file mode 100644 index 00000000..6364bac4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/__init__.pyi @@ -0,0 +1,314 @@ +import sys + +from typing import ( + Any, + Callable, + Mapping, + Sequence, + overload, + TypeVar, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import AttrsInstance as AttrsInstance +from attr import cmp_using as cmp_using +from attr import converters as converters +from attr import Converter as Converter +from attr import evolve as evolve +from attr import exceptions as exceptions +from attr import Factory as Factory +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import filters as filters +from attr import has as has +from attr import make_class as make_class +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators +from attr import attrib, asdict as asdict, astuple as astuple +from attr import NothingType as NothingType + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = bool | Callable[[Any], Any] +_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] +_CallableConverterType = Callable[[Any], Any] +_ConverterType = _CallableConverterType | Converter[Any, Any] +_ReprType = Callable[[Any], str] +_ReprArgType = bool | _ReprType +_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] +_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType +_FieldTransformer = Callable[ + [type, list["Attribute[Any]"]], list["Attribute[Any]"] +] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]] + +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: bool | None = ..., + order: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define + +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +class ClassProps: + # XXX: somehow when defining/using enums Mypy starts looking at our own + # (untyped) code and causes tons of errors. + Hashability: Any + KeywordOnly: Any + + is_exception: bool + is_slotted: bool + has_weakref_slot: bool + is_frozen: bool + # kw_only: ClassProps.KeywordOnly + kw_only: Any + collected_fields_by_mro: bool + added_init: bool + added_repr: bool + added_eq: bool + added_ordering: bool + # hashability: ClassProps.Hashability + hashability: Any + added_match_args: bool + added_str: bool + added_pickling: bool + on_setattr_hook: _OnSetAttrType | None + field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None + + def __init__( + self, + is_exception: bool, + is_slotted: bool, + has_weakref_slot: bool, + is_frozen: bool, + # kw_only: ClassProps.KeywordOnly + kw_only: Any, + collected_fields_by_mro: bool, + added_init: bool, + added_repr: bool, + added_eq: bool, + added_ordering: bool, + # hashability: ClassProps.Hashability + hashability: Any, + added_match_args: bool, + added_str: bool, + added_pickling: bool, + on_setattr_hook: _OnSetAttrType, + field_transformer: Callable[[Attribute[Any]], Attribute[Any]], + ) -> None: ... + @property + def is_hashable(self) -> bool: ... + +def inspect(cls: type) -> ClassProps: ... diff --git a/.venv/lib/python3.11/site-packages/attrs/converters.py b/.venv/lib/python3.11/site-packages/attrs/converters.py new file mode 100644 index 00000000..7821f6c0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa: F403 diff --git a/.venv/lib/python3.11/site-packages/attrs/exceptions.py b/.venv/lib/python3.11/site-packages/attrs/exceptions.py new file mode 100644 index 00000000..3323f9d2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa: F403 diff --git a/.venv/lib/python3.11/site-packages/attrs/filters.py b/.venv/lib/python3.11/site-packages/attrs/filters.py new file mode 100644 index 00000000..3080f483 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa: F403 diff --git a/.venv/lib/python3.11/site-packages/attrs/py.typed b/.venv/lib/python3.11/site-packages/attrs/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/attrs/setters.py b/.venv/lib/python3.11/site-packages/attrs/setters.py new file mode 100644 index 00000000..f3d73bb7 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa: F403 diff --git a/.venv/lib/python3.11/site-packages/attrs/validators.py b/.venv/lib/python3.11/site-packages/attrs/validators.py new file mode 100644 index 00000000..037e124f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa: F403 diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA new file mode 100644 index 00000000..7fd97b76 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.4 +Name: beautifulsoup4 +Version: 4.14.3 +Summary: Screen-scraping library +Project-URL: Download, https://www.crummy.com/software/BeautifulSoup/bs4/download/ +Project-URL: Homepage, https://www.crummy.com/software/BeautifulSoup/bs4/ +Author-email: Leonard Richardson +License: MIT License +License-File: AUTHORS +License-File: LICENSE +Keywords: HTML,XML,parse,soup +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: SGML +Classifier: Topic :: Text Processing :: Markup :: XML +Requires-Python: >=3.7.0 +Requires-Dist: soupsieve>=1.6.1 +Requires-Dist: typing-extensions>=4.0.0 +Provides-Extra: cchardet +Requires-Dist: cchardet; extra == 'cchardet' +Provides-Extra: chardet +Requires-Dist: chardet; extra == 'chardet' +Provides-Extra: charset-normalizer +Requires-Dist: charset-normalizer; extra == 'charset-normalizer' +Provides-Extra: html5lib +Requires-Dist: html5lib; extra == 'html5lib' +Provides-Extra: lxml +Requires-Dist: lxml; extra == 'lxml' +Description-Content-Type: text/markdown + +Beautiful Soup is a library that makes it easy to scrape information +from web pages. It sits atop an HTML or XML parser, providing Pythonic +idioms for iterating, searching, and modifying the parse tree. + +# Quick start + +``` +>>> from bs4 import BeautifulSoup +>>> soup = BeautifulSoup("

    SomebadHTML") +>>> print(soup.prettify()) + + +

    + Some + + bad + + HTML + + +

    + + +>>> soup.find(string="bad") +'bad' +>>> soup.i +HTML +# +>>> soup = BeautifulSoup("SomebadXML", "xml") +# +>>> print(soup.prettify()) + + + Some + + bad + + XML + + +``` + +To go beyond the basics, [comprehensive documentation is available](https://www.crummy.com/software/BeautifulSoup/bs4/doc/). + +# Links + +* [Homepage](https://www.crummy.com/software/BeautifulSoup/bs4/) +* [Documentation](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) +* [Discussion group](https://groups.google.com/group/beautifulsoup/) +* [Development](https://code.launchpad.net/beautifulsoup/) +* [Bug tracker](https://bugs.launchpad.net/beautifulsoup/) +* [Complete changelog](https://git.launchpad.net/beautifulsoup/tree/CHANGELOG) + +# Note on Python 2 sunsetting + +Beautiful Soup's support for Python 2 was discontinued on December 31, +2020: one year after the sunset date for Python 2 itself. From this +point onward, new Beautiful Soup development will exclusively target +Python 3. The final release of Beautiful Soup 4 to support Python 2 +was 4.9.3. + +# Supporting the project + +If you use Beautiful Soup as part of your professional work, please consider a +[Tidelift subscription](https://tidelift.com/subscription/pkg/pypi-beautifulsoup4?utm_source=pypi-beautifulsoup4&utm_medium=referral&utm_campaign=readme). +This will support many of the free software projects your organization +depends on, not just Beautiful Soup. + +If you use Beautiful Soup for personal projects, the best way to say +thank you is to read +[Tool Safety](https://www.crummy.com/software/BeautifulSoup/zine/), a zine I +wrote about what Beautiful Soup has taught me about software +development. + +# Building the documentation + +The bs4/doc/ directory contains full documentation in Sphinx +format. Run `make html` in that directory to create HTML +documentation. + +# Running the unit tests + +Beautiful Soup supports unit test discovery using Pytest: + +``` +$ pytest +``` + diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD new file mode 100644 index 00000000..8a5523a1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD @@ -0,0 +1,38 @@ +beautifulsoup4-4.14.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +beautifulsoup4-4.14.3.dist-info/METADATA,sha256=Ac93vA8Xp9FtgOcKXFM8ESfVdztimUfJ3WUpVlhKtsY,3812 +beautifulsoup4-4.14.3.dist-info/RECORD,, +beautifulsoup4-4.14.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +beautifulsoup4-4.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS,sha256=uYkjiRjh_aweRnF8tAW2PpJJeickE68NmJwd9siry28,2201 +beautifulsoup4-4.14.3.dist-info/licenses/LICENSE,sha256=VbTY1LHlvIbRDvrJG3TIe8t3UmsPW57a-LnNKtxzl7I,1441 +bs4/__init__.py,sha256=E7wiVp7oQK0JhdAYxpehZa8drv3W_sJv5oeTFiBfR5o,44386 +bs4/__pycache__/__init__.cpython-311.pyc,, +bs4/__pycache__/_deprecation.cpython-311.pyc,, +bs4/__pycache__/_typing.cpython-311.pyc,, +bs4/__pycache__/_warnings.cpython-311.pyc,, +bs4/__pycache__/css.cpython-311.pyc,, +bs4/__pycache__/dammit.cpython-311.pyc,, +bs4/__pycache__/diagnose.cpython-311.pyc,, +bs4/__pycache__/element.cpython-311.pyc,, +bs4/__pycache__/exceptions.cpython-311.pyc,, +bs4/__pycache__/filter.cpython-311.pyc,, +bs4/__pycache__/formatter.cpython-311.pyc,, +bs4/_deprecation.py,sha256=niHJCk37APg8KEuFOa57ZXaxLdBmc_2V6uuaJqu7r30,2408 +bs4/_typing.py,sha256=zNcx7R1yCTK8WwtumP28hc7CJ3pMyZXj_VAeYaNXMZA,7549 +bs4/_warnings.py,sha256=ZuOETgcnEbZgw2N0nnNXn6wvtrn2ut7AF0d98bvkMFc,4711 +bs4/builder/__init__.py,sha256=Rl4qjOXvdyyyjayOFqbkgoUoo81IgoyKD-RwWeVK59g,31194 +bs4/builder/__pycache__/__init__.cpython-311.pyc,, +bs4/builder/__pycache__/_html5lib.cpython-311.pyc,, +bs4/builder/__pycache__/_htmlparser.cpython-311.pyc,, +bs4/builder/__pycache__/_lxml.cpython-311.pyc,, +bs4/builder/_html5lib.py,sha256=hL6xUk4_I2i5CMguFoYFlrI26cY4Dut7fOEQrUctHIM,23607 +bs4/builder/_htmlparser.py,sha256=CnULPQV2rm4vLojJABpQ7Xm9diddnEZx2Wcz_VTC1Mg,17445 +bs4/builder/_lxml.py,sha256=ks1e8boA_nOA2oomAhxeudccR6ThbEE-EllFqHRoPLA,18969 +bs4/css.py,sha256=_m_l_4SGWHnY620VJ21j_qQH1RX3p91sYVemgKxaLsM,12713 +bs4/dammit.py,sha256=ZJWa9K32X6N2imFHleqUq0ekf592weU1lvULN_WYWYk,57024 +bs4/diagnose.py,sha256=at98iuxyOrqec4V8iwkTIbNUqBCsq9Lr3fDAQx2129Y,7846 +bs4/element.py,sha256=oXmj7LG_2NpsDK90mq73q0PMK0FjFBIGSeTTJLVwwTc,120237 +bs4/exceptions.py,sha256=Q9FOadNe8QRvzDMaKSXe2Wtl8JK_oAZW7mbFZBVP_GE,951 +bs4/filter.py,sha256=rw8ZNhTDLEJVCEiSifou5tZR_3zBLeuvAyouY82qU_E,29201 +bs4/formatter.py,sha256=uBT0k6W8O5kJ9PCuJYjra97yoUqC-dlM9D_v-oRM0r8,10478 +bs4/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL new file mode 100644 index 00000000..12228d41 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS new file mode 100644 index 00000000..18926c29 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS @@ -0,0 +1,49 @@ +Behold, mortal, the origins of Beautiful Soup... +================================================ + +Leonard Richardson is the primary maintainer. + +Aaron DeVore, Isaac Muse and Chris Papademetrious have made +significant contributions to the code base. + +Mark Pilgrim provided the encoding detection code that forms the base +of UnicodeDammit. + +Thomas Kluyver and Ezio Melotti finished the work of getting Beautiful +Soup 4 working under Python 3. + +Simon Willison wrote soupselect, which was used to make Beautiful Soup +support CSS selectors. Isaac Muse wrote SoupSieve, which made it +possible to _remove_ the CSS selector code from Beautiful Soup. + +Sam Ruby helped with a lot of edge cases. + +Jonathan Ellis was awarded the prestigious Beau Potage D'Or for his +work in solving the nestable tags conundrum. + +An incomplete list of people have contributed patches to Beautiful +Soup: + + Istvan Albert, Andrew Lin, Anthony Baxter, Oliver Beattie, Andrew +Boyko, Tony Chang, Francisco Canas, "Delong", Zephyr Fang, Fuzzy, +Roman Gaufman, Yoni Gilad, Richie Hindle, Toshihiro Kamiya, Peteris +Krumins, Kent Johnson, Marek Kapolka, Andreas Kostyrka, Roel Kramer, +Ben Last, Robert Leftwich, Stefaan Lippens, "liquider", Staffan +Malmgren, Ksenia Marasanova, JP Moins, Adam Monsen, John Nagle, "Jon", +Ed Oskiewicz, Martijn Peters, Greg Phillips, Giles Radford, Stefano +Revera, Arthur Rudolph, Marko Samastur, James Salter, Jouni Seppänen, +Alexander Schmolck, Tim Shirley, Geoffrey Sneddon, Ville Skyttä, +"Vikas", Jens Svalgaard, Andy Theyers, Eric Weiser, Glyn Webster, John +Wiseman, Paul Wright, Danny Yoo + +An incomplete list of people who made suggestions or found bugs or +found ways to break Beautiful Soup: + + Hanno Böck, Matteo Bertini, Chris Curvey, Simon Cusack, Bruce Eckel, + Matt Ernst, Michael Foord, Tom Harris, Bill de hOra, Donald Howes, + Matt Patterson, Scott Roberts, Steve Strassmann, Mike Williams, + warchild at redho dot com, Sami Kuisma, Carlos Rocha, Bob Hutchison, + Joren Mc, Michal Migurski, John Kleven, Tim Heaney, Tripp Lilley, Ed + Summers, Dennis Sutch, Chris Smith, Aaron Swartz, Stuart + Turner, Greg Edwards, Kevin J Kalupson, Nikos Kouremenos, Artur de + Sousa Rocha, Yichun Wei, Per Vognsen diff --git a/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE new file mode 100644 index 00000000..08e3a9cf --- /dev/null +++ b/.venv/lib/python3.11/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE @@ -0,0 +1,31 @@ +Beautiful Soup is made available under the MIT license: + + Copyright (c) Leonard Richardson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Beautiful Soup incorporates code from the html5lib library, which is +also made available under the MIT license. Copyright (c) James Graham +and other contributors + +Beautiful Soup has an optional dependency on the soupsieve library, +which is also made available under the MIT license. Copyright (c) +Isaac Muse diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/METADATA new file mode 100644 index 00000000..d0a9db0d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/METADATA @@ -0,0 +1,1610 @@ +Metadata-Version: 2.1 +Name: black +Version: 23.1.0 +Summary: The uncompromising code formatter. +Project-URL: Changelog, https://github.com/psf/black/blob/main/CHANGES.md +Project-URL: Homepage, https://github.com/psf/black +Author-email: Łukasz Langa +License: MIT +License-File: AUTHORS.md +License-File: LICENSE +Keywords: automation,autopep8,formatter,gofmt,pyfmt,rustfmt,yapf +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Requires-Python: >=3.7 +Requires-Dist: click>=8.0.0 +Requires-Dist: mypy-extensions>=0.4.3 +Requires-Dist: packaging>=22.0 +Requires-Dist: pathspec>=0.9.0 +Requires-Dist: platformdirs>=2 +Requires-Dist: tomli>=1.1.0; python_version < '3.11' +Requires-Dist: typed-ast>=1.4.2; python_version < '3.8' and implementation_name == 'cpython' +Requires-Dist: typing-extensions>=3.10.0.0; python_version < '3.10' +Provides-Extra: colorama +Requires-Dist: colorama>=0.4.3; extra == 'colorama' +Provides-Extra: d +Requires-Dist: aiohttp>=3.7.4; extra == 'd' +Provides-Extra: jupyter +Requires-Dist: ipython>=7.8.0; extra == 'jupyter' +Requires-Dist: tokenize-rt>=3.2.0; extra == 'jupyter' +Provides-Extra: uvloop +Requires-Dist: uvloop>=0.15.2; extra == 'uvloop' +Description-Content-Type: text/markdown + +[![Black Logo](https://raw.githubusercontent.com/psf/black/main/docs/_static/logo2-readme.png)](https://black.readthedocs.io/en/stable/) + +

    The Uncompromising Code Formatter

    + +

    +Actions Status +Documentation Status +Coverage Status +License: MIT +PyPI +Downloads +conda-forge +Code style: black +

    + +> “Any color you like.” + +_Black_ is the uncompromising Python code formatter. By using it, you agree to cede +control over minutiae of hand-formatting. In return, _Black_ gives you speed, +determinism, and freedom from `pycodestyle` nagging about formatting. You will save time +and mental energy for more important matters. + +Blackened code looks the same regardless of the project you're reading. Formatting +becomes transparent after a while and you can focus on the content instead. + +_Black_ makes code review faster by producing the smallest diffs possible. + +Try it out now using the [Black Playground](https://black.vercel.app). Watch the +[PyCon 2019 talk](https://youtu.be/esZLCuWs_2Y) to learn more. + +--- + +**[Read the documentation on ReadTheDocs!](https://black.readthedocs.io/en/stable)** + +--- + +## Installation and usage + +### Installation + +_Black_ can be installed by running `pip install black`. It requires Python 3.7+ to run. +If you want to format Jupyter Notebooks, install with `pip install "black[jupyter]"`. + +If you can't wait for the latest _hotness_ and want to install from GitHub, use: + +`pip install git+https://github.com/psf/black` + +### Usage + +To get started right away with sensible defaults: + +```sh +black {source_file_or_directory} +``` + +You can run _Black_ as a package if running it as a script doesn't work: + +```sh +python -m black {source_file_or_directory} +``` + +Further information can be found in our docs: + +- [Usage and Configuration](https://black.readthedocs.io/en/stable/usage_and_configuration/index.html) + +_Black_ is already [successfully used](https://github.com/psf/black#used-by) by many +projects, small and big. _Black_ has a comprehensive test suite, with efficient parallel +tests, and our own auto formatting and parallel Continuous Integration runner. Now that +we have become stable, you should not expect large formatting changes in the future. +Stylistic changes will mostly be responses to bug reports and support for new Python +syntax. For more information please refer to the +[The Black Code Style](https://black.readthedocs.io/en/stable/the_black_code_style/index.html). + +Also, as a safety measure which slows down processing, _Black_ will check that the +reformatted code still produces a valid AST that is effectively equivalent to the +original (see the +[Pragmatism](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#ast-before-and-after-formatting) +section for details). If you're feeling confident, use `--fast`. + +## The _Black_ code style + +_Black_ is a PEP 8 compliant opinionated formatter. _Black_ reformats entire files in +place. Style configuration options are deliberately limited and rarely added. It doesn't +take previous formatting into account (see +[Pragmatism](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#pragmatism) +for exceptions). + +Our documentation covers the current _Black_ code style, but planned changes to it are +also documented. They're both worth taking a look: + +- [The _Black_ Code Style: Current style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html) +- [The _Black_ Code Style: Future style](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html) + +Changes to the _Black_ code style are bound by the Stability Policy: + +- [The _Black_ Code Style: Stability Policy](https://black.readthedocs.io/en/stable/the_black_code_style/index.html#stability-policy) + +Please refer to this document before submitting an issue. What seems like a bug might be +intended behaviour. + +### Pragmatism + +Early versions of _Black_ used to be absolutist in some respects. They took after its +initial author. This was fine at the time as it made the implementation simpler and +there were not many users anyway. Not many edge cases were reported. As a mature tool, +_Black_ does make some exceptions to rules it otherwise holds. + +- [The _Black_ code style: Pragmatism](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#pragmatism) + +Please refer to this document before submitting an issue just like with the document +above. What seems like a bug might be intended behaviour. + +## Configuration + +_Black_ is able to read project-specific default values for its command line options +from a `pyproject.toml` file. This is especially useful for specifying custom +`--include` and `--exclude`/`--force-exclude`/`--extend-exclude` patterns for your +project. + +You can find more details in our documentation: + +- [The basics: Configuration via a file](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file) + +And if you're looking for more general configuration documentation: + +- [Usage and Configuration](https://black.readthedocs.io/en/stable/usage_and_configuration/index.html) + +**Pro-tip**: If you're asking yourself "Do I need to configure anything?" the answer is +"No". _Black_ is all about sensible defaults. Applying those defaults will have your +code in compliance with many other _Black_ formatted projects. + +## Used by + +The following notable open-source projects trust _Black_ with enforcing a consistent +code style: pytest, tox, Pyramid, Django, Django Channels, Hypothesis, attrs, +SQLAlchemy, Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), +pandas, Pillow, Twisted, LocalStack, every Datadog Agent Integration, Home Assistant, +Zulip, Kedro, OpenOA, FLORIS, ORBIT, WOMBAT, and many more. + +The following organizations use _Black_: Facebook, Dropbox, KeepTruckin, Mozilla, Quora, +Duolingo, QuantumBlack, Tesla, Archer Aviation. + +Are we missing anyone? Let us know. + +## Testimonials + +**Mike Bayer**, [author of `SQLAlchemy`](https://www.sqlalchemy.org/): + +> I can't think of any single tool in my entire programming career that has given me a +> bigger productivity increase by its introduction. I can now do refactorings in about +> 1% of the keystrokes that it would have taken me previously when we had no way for +> code to format itself. + +**Dusty Phillips**, +[writer](https://smile.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=dusty+phillips): + +> _Black_ is opinionated so you don't have to be. + +**Hynek Schlawack**, [creator of `attrs`](https://www.attrs.org/), core developer of +Twisted and CPython: + +> An auto-formatter that doesn't suck is all I want for Xmas! + +**Carl Meyer**, [Django](https://www.djangoproject.com/) core developer: + +> At least the name is good. + +**Kenneth Reitz**, creator of [`requests`](https://requests.readthedocs.io/en/latest/) +and [`pipenv`](https://readthedocs.org/projects/pipenv/): + +> This vastly improves the formatting of our code. Thanks a ton! + +## Show your style + +Use the badge in your project's README.md: + +```md +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +``` + +Using the badge in README.rst: + +``` +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black +``` + +Looks like this: +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +## License + +MIT + +## Contributing + +Welcome! Happy to see you willing to make the project better. You can get started by +reading this: + +- [Contributing: The basics](https://black.readthedocs.io/en/latest/contributing/the_basics.html) + +You can also take a look at the rest of the contributing docs or talk with the +developers: + +- [Contributing documentation](https://black.readthedocs.io/en/latest/contributing/index.html) +- [Chat on Discord](https://discord.gg/RtVdv86PrH) + +## Change log + +The log has become rather long. It moved to its own file. + +See [CHANGES](https://black.readthedocs.io/en/latest/change_log.html). + +## Authors + +The author list is quite long nowadays, so it lives in its own file. + +See [AUTHORS.md](./AUTHORS.md) + +## Code of Conduct + +Everyone participating in the _Black_ project, and in particular in the issue tracker, +pull requests, and social media activity, is expected to treat other people with respect +and more generally to follow the guidelines articulated in the +[Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/). + +At the same time, humor is encouraged. In fact, basic familiarity with Monty Python's +Flying Circus is expected. We are not savages. + +And if you _really_ need to slap somebody, do it with a fish while dancing. +# Change Log + +## Unreleased + +### Highlights + + + +### Stable style + + + +### Preview style + + + +### Configuration + + + +### Packaging + + + +### Parser + + + +### Performance + + + +### Output + + + +### _Blackd_ + + + +### Integrations + + + +### Documentation + + + +## 23.1.0 + +### Highlights + +This is the first release of 2023, and following our +[stability policy](https://black.readthedocs.io/en/stable/the_black_code_style/index.html#stability-policy), +it comes with a number of improvements to our stable style, including improvements to +empty line handling, removal of redundant parentheses in several contexts, and output +that highlights implicitly concatenated strings better. + +There are also many changes to the preview style; try out `black --preview` and give us +feedback to help us set the stable style for next year. + +In addition to style changes, Black now automatically infers the supported Python +versions from your `pyproject.toml` file, removing the need to set Black's target +versions separately. + +### Stable style + + + +- Introduce the 2023 stable style, which incorporates most aspects of last year's + preview style (#3418). Specific changes: + - Enforce empty lines before classes and functions with sticky leading comments + (#3302) (22.12.0) + - Reformat empty and whitespace-only files as either an empty file (if no newline is + present) or as a single newline character (if a newline is present) (#3348) + (22.12.0) + - Implicitly concatenated strings used as function args are now wrapped inside + parentheses (#3307) (22.12.0) + - Correctly handle trailing commas that are inside a line's leading non-nested parens + (#3370) (22.12.0) + - `--skip-string-normalization` / `-S` now prevents docstring prefixes from being + normalized as expected (#3168) (since 22.8.0) + - When using `--skip-magic-trailing-comma` or `-C`, trailing commas are stripped from + subscript expressions with more than 1 element (#3209) (22.8.0) + - Implicitly concatenated strings inside a list, set, or tuple are now wrapped inside + parentheses (#3162) (22.8.0) + - Fix a string merging/split issue when a comment is present in the middle of + implicitly concatenated strings on its own line (#3227) (22.8.0) + - Docstring quotes are no longer moved if it would violate the line length limit + (#3044, #3430) (22.6.0) + - Parentheses around return annotations are now managed (#2990) (22.6.0) + - Remove unnecessary parentheses around awaited objects (#2991) (22.6.0) + - Remove unnecessary parentheses in `with` statements (#2926) (22.6.0) + - Remove trailing newlines after code block open (#3035) (22.6.0) + - Code cell separators `#%%` are now standardised to `# %%` (#2919) (22.3.0) + - Remove unnecessary parentheses from `except` statements (#2939) (22.3.0) + - Remove unnecessary parentheses from tuple unpacking in `for` loops (#2945) (22.3.0) + - Avoid magic-trailing-comma in single-element subscripts (#2942) (22.3.0) +- Fix a crash when a colon line is marked between `# fmt: off` and `# fmt: on` (#3439) + +### Preview style + + + +- Format hex codes in unicode escape sequences in string literals (#2916) +- Add parentheses around `if`-`else` expressions (#2278) +- Improve performance on large expressions that contain many strings (#3467) +- Fix a crash in preview style with assert + parenthesized string (#3415) +- Fix crashes in preview style with walrus operators used in function return annotations + and except clauses (#3423) +- Fix a crash in preview advanced string processing where mixed implicitly concatenated + regular and f-strings start with an empty span (#3463) +- Fix a crash in preview advanced string processing where a standalone comment is placed + before a dict's value (#3469) +- Fix an issue where extra empty lines are added when a decorator has `# fmt: skip` + applied or there is a standalone comment between decorators (#3470) +- Do not put the closing quotes in a docstring on a separate line, even if the line is + too long (#3430) +- Long values in dict literals are now wrapped in parentheses; correspondingly + unnecessary parentheses around short values in dict literals are now removed; long + string lambda values are now wrapped in parentheses (#3440) +- Fix two crashes in preview style involving edge cases with docstrings (#3451) +- Exclude string type annotations from improved string processing; fix crash when the + return type annotation is stringified and spans across multiple lines (#3462) +- Wrap multiple context managers in parentheses when targeting Python 3.9+ (#3489) +- Fix several crashes in preview style with walrus operators used in `with` statements + or tuples (#3473) +- Fix an invalid quote escaping bug in f-string expressions where it produced invalid + code. Implicitly concatenated f-strings with different quotes can now be merged or + quote-normalized by changing the quotes used in expressions. (#3509) +- Fix crash on `await (yield)` when Black is compiled with mypyc (#3533) + +### Configuration + + + +- Black now tries to infer its `--target-version` from the project metadata specified in + `pyproject.toml` (#3219) + +### Packaging + + + +- Upgrade mypyc from `0.971` to `0.991` so mypycified _Black_ can be built on armv7 + (#3380) + - This also fixes some crashes while using compiled Black with a debug build of + CPython +- Drop specific support for the `tomli` requirement on 3.11 alpha releases, working + around a bug that would cause the requirement not to be installed on any non-final + Python releases (#3448) +- Black now depends on `packaging` version `22.0` or later. This is required for new + functionality that needs to parse part of the project metadata (#3219) + +### Output + + + +- Calling `black --help` multiple times will return the same help contents each time + (#3516) +- Verbose logging now shows the values of `pyproject.toml` configuration variables + (#3392) +- Fix false symlink detection messages in verbose output due to using an incorrect + relative path to the project root (#3385) + +### Integrations + + + +- Move 3.11 CI to normal flow now that all dependencies support 3.11 (#3446) +- Docker: Add new `latest_prerelease` tag automation to follow latest black alpha + release on docker images (#3465) + +### Documentation + + + +- Expand `vim-plug` installation instructions to offer more explicit options (#3468) + +## 22.12.0 + +### Preview style + + + +- Enforce empty lines before classes and functions with sticky leading comments (#3302) +- Reformat empty and whitespace-only files as either an empty file (if no newline is + present) or as a single newline character (if a newline is present) (#3348) +- Implicitly concatenated strings used as function args are now wrapped inside + parentheses (#3307) +- For assignment statements, prefer splitting the right hand side if the left hand side + fits on a single line (#3368) +- Correctly handle trailing commas that are inside a line's leading non-nested parens + (#3370) + +### Configuration + + + +- Fix incorrectly applied `.gitignore` rules by considering the `.gitignore` location + and the relative path to the target file (#3338) +- Fix incorrectly ignoring `.gitignore` presence when more than one source directory is + specified (#3336) + +### Parser + + + +- Parsing support has been added for walruses inside generator expression that are + passed as function args (for example, + `any(match := my_re.match(text) for text in texts)`) (#3327). + +### Integrations + + + +- Vim plugin: Optionally allow using the system installation of Black via + `let g:black_use_virtualenv = 0`(#3309) + +## 22.10.0 + +### Highlights + +- Runtime support for Python 3.6 has been removed. Formatting 3.6 code will still be + supported until further notice. + +### Stable style + +- Fix a crash when `# fmt: on` is used on a different block level than `# fmt: off` + (#3281) + +### Preview style + +- Fix a crash when formatting some dicts with parenthesis-wrapped long string keys + (#3262) + +### Configuration + +- `.ipynb_checkpoints` directories are now excluded by default (#3293) +- Add `--skip-source-first-line` / `-x` option to ignore the first line of source code + while formatting (#3299) + +### Packaging + +- Executables made with PyInstaller will no longer crash when formatting several files + at once on macOS. Native x86-64 executables for macOS are available once again. + (#3275) +- Hatchling is now used as the build backend. This will not have any effect for users + who install Black with its wheels from PyPI. (#3233) +- Faster compiled wheels are now available for CPython 3.11 (#3276) + +### _Blackd_ + +- Windows style (CRLF) newlines will be preserved (#3257). + +### Integrations + +- Vim plugin: add flag (`g:black_preview`) to enable/disable the preview style (#3246) +- Update GitHub Action to support formatting of Jupyter Notebook files via a `jupyter` + option (#3282) +- Update GitHub Action to support use of version specifiers (e.g. `<23`) for Black + version (#3265) + +## 22.8.0 + +### Highlights + +- Python 3.11 is now supported, except for _blackd_ as aiohttp does not support 3.11 as + of publishing (#3234) +- This is the last release that supports running _Black_ on Python 3.6 (formatting 3.6 + code will continue to be supported until further notice) +- Reword the stability policy to say that we may, in rare cases, make changes that + affect code that was not previously formatted by _Black_ (#3155) + +### Stable style + +- Fix an infinite loop when using `# fmt: on/off` in the middle of an expression or code + block (#3158) +- Fix incorrect handling of `# fmt: skip` on colon (`:`) lines (#3148) +- Comments are no longer deleted when a line had spaces removed around power operators + (#2874) + +### Preview style + +- Single-character closing docstring quotes are no longer moved to their own line as + this is invalid. This was a bug introduced in version 22.6.0. (#3166) +- `--skip-string-normalization` / `-S` now prevents docstring prefixes from being + normalized as expected (#3168) +- When using `--skip-magic-trailing-comma` or `-C`, trailing commas are stripped from + subscript expressions with more than 1 element (#3209) +- Implicitly concatenated strings inside a list, set, or tuple are now wrapped inside + parentheses (#3162) +- Fix a string merging/split issue when a comment is present in the middle of implicitly + concatenated strings on its own line (#3227) + +### _Blackd_ + +- `blackd` now supports enabling the preview style via the `X-Preview` header (#3217) + +### Configuration + +- Black now uses the presence of debug f-strings to detect target version (#3215) +- Fix misdetection of project root and verbose logging of sources in cases involving + `--stdin-filename` (#3216) +- Immediate `.gitignore` files in source directories given on the command line are now + also respected, previously only `.gitignore` files in the project root and + automatically discovered directories were respected (#3237) + +### Documentation + +- Recommend using BlackConnect in IntelliJ IDEs (#3150) + +### Integrations + +- Vim plugin: prefix messages with `Black: ` so it's clear they come from Black (#3194) +- Docker: changed to a /opt/venv installation + added to PATH to be available to + non-root users (#3202) + +### Output + +- Change from deprecated `asyncio.get_event_loop()` to create our event loop which + removes DeprecationWarning (#3164) +- Remove logging from internal `blib2to3` library since it regularly emits error logs + about failed caching that can and should be ignored (#3193) + +### Parser + +- Type comments are now included in the AST equivalence check consistently so accidental + deletion raises an error. Though type comments can't be tracked when running on PyPy + 3.7 due to standard library limitations. (#2874) + +### Performance + +- Reduce Black's startup time when formatting a single file by 15-30% (#3211) + +## 22.6.0 + +### Style + +- Fix unstable formatting involving `#fmt: skip` and `# fmt:skip` comments (notice the + lack of spaces) (#2970) + +### Preview style + +- Docstring quotes are no longer moved if it would violate the line length limit (#3044) +- Parentheses around return annotations are now managed (#2990) +- Remove unnecessary parentheses around awaited objects (#2991) +- Remove unnecessary parentheses in `with` statements (#2926) +- Remove trailing newlines after code block open (#3035) + +### Integrations + +- Add `scripts/migrate-black.py` script to ease introduction of Black to a Git project + (#3038) + +### Output + +- Output Python version and implementation as part of `--version` flag (#2997) + +### Packaging + +- Use `tomli` instead of `tomllib` on Python 3.11 builds where `tomllib` is not + available (#2987) + +### Parser + +- [PEP 654](https://peps.python.org/pep-0654/#except) syntax (for example, + `except *ExceptionGroup:`) is now supported (#3016) +- [PEP 646](https://peps.python.org/pep-0646) syntax (for example, + `Array[Batch, *Shape]` or `def fn(*args: *T) -> None`) is now supported (#3071) + +### Vim Plugin + +- Fix `strtobool` function. It didn't parse true/on/false/off. (#3025) + +## 22.3.0 + +### Preview style + +- Code cell separators `#%%` are now standardised to `# %%` (#2919) +- Remove unnecessary parentheses from `except` statements (#2939) +- Remove unnecessary parentheses from tuple unpacking in `for` loops (#2945) +- Avoid magic-trailing-comma in single-element subscripts (#2942) + +### Configuration + +- Do not format `__pypackages__` directories by default (#2836) +- Add support for specifying stable version with `--required-version` (#2832). +- Avoid crashing when the user has no homedir (#2814) +- Avoid crashing when md5 is not available (#2905) +- Fix handling of directory junctions on Windows (#2904) + +### Documentation + +- Update pylint config documentation (#2931) + +### Integrations + +- Move test to disable plugin in Vim/Neovim, which speeds up loading (#2896) + +### Output + +- In verbose mode, log when _Black_ is using user-level config (#2861) + +### Packaging + +- Fix Black to work with Click 8.1.0 (#2966) +- On Python 3.11 and newer, use the standard library's `tomllib` instead of `tomli` + (#2903) +- `black-primer`, the deprecated internal devtool, has been removed and copied to a + [separate repository](https://github.com/cooperlees/black-primer) (#2924) + +### Parser + +- Black can now parse starred expressions in the target of `for` and `async for` + statements, e.g `for item in *items_1, *items_2: pass` (#2879). + +## 22.1.0 + +At long last, _Black_ is no longer a beta product! This is the first non-beta release +and the first release covered by our new +[stability policy](https://black.readthedocs.io/en/stable/the_black_code_style/index.html#stability-policy). + +### Highlights + +- **Remove Python 2 support** (#2740) +- Introduce the `--preview` flag (#2752) + +### Style + +- Deprecate `--experimental-string-processing` and move the functionality under + `--preview` (#2789) +- For stubs, one blank line between class attributes and methods is now kept if there's + at least one pre-existing blank line (#2736) +- Black now normalizes string prefix order (#2297) +- Remove spaces around power operators if both operands are simple (#2726) +- Work around bug that causes unstable formatting in some cases in the presence of the + magic trailing comma (#2807) +- Use parentheses for attribute access on decimal float and int literals (#2799) +- Don't add whitespace for attribute access on hexadecimal, binary, octal, and complex + literals (#2799) +- Treat blank lines in stubs the same inside top-level `if` statements (#2820) +- Fix unstable formatting with semicolons and arithmetic expressions (#2817) +- Fix unstable formatting around magic trailing comma (#2572) + +### Parser + +- Fix mapping cases that contain as-expressions, like `case {"key": 1 | 2 as password}` + (#2686) +- Fix cases that contain multiple top-level as-expressions, like `case 1 as a, 2 as b` + (#2716) +- Fix call patterns that contain as-expressions with keyword arguments, like + `case Foo(bar=baz as quux)` (#2749) +- Tuple unpacking on `return` and `yield` constructs now implies 3.8+ (#2700) +- Unparenthesized tuples on annotated assignments (e.g + `values: Tuple[int, ...] = 1, 2, 3`) now implies 3.8+ (#2708) +- Fix handling of standalone `match()` or `case()` when there is a trailing newline or a + comment inside of the parentheses. (#2760) +- `from __future__ import annotations` statement now implies Python 3.7+ (#2690) + +### Performance + +- Speed-up the new backtracking parser about 4X in general (enabled when + `--target-version` is set to 3.10 and higher). (#2728) +- _Black_ is now compiled with [mypyc](https://github.com/mypyc/mypyc) for an overall 2x + speed-up. 64-bit Windows, MacOS, and Linux (not including musl) are supported. (#1009, + #2431) + +### Configuration + +- Do not accept bare carriage return line endings in pyproject.toml (#2408) +- Add configuration option (`python-cell-magics`) to format cells with custom magics in + Jupyter Notebooks (#2744) +- Allow setting custom cache directory on all platforms with environment variable + `BLACK_CACHE_DIR` (#2739). +- Enable Python 3.10+ by default, without any extra need to specify + `--target-version=py310`. (#2758) +- Make passing `SRC` or `--code` mandatory and mutually exclusive (#2804) + +### Output + +- Improve error message for invalid regular expression (#2678) +- Improve error message when parsing fails during AST safety check by embedding the + underlying SyntaxError (#2693) +- No longer color diff headers white as it's unreadable in light themed terminals + (#2691) +- Text coloring added in the final statistics (#2712) +- Verbose mode also now describes how a project root was discovered and which paths will + be formatted. (#2526) + +### Packaging + +- All upper version bounds on dependencies have been removed (#2718) +- `typing-extensions` is no longer a required dependency in Python 3.10+ (#2772) +- Set `click` lower bound to `8.0.0` (#2791) + +### Integrations + +- Update GitHub action to support containerized runs (#2748) + +### Documentation + +- Change protocol in pip installation instructions to `https://` (#2761) +- Change HTML theme to Furo primarily for its responsive design and mobile support + (#2793) +- Deprecate the `black-primer` tool (#2809) +- Document Python support policy (#2819) + +## 21.12b0 + +### _Black_ + +- Fix determination of f-string expression spans (#2654) +- Fix bad formatting of error messages about EOF in multi-line statements (#2343) +- Functions and classes in blocks now have more consistent surrounding spacing (#2472) + +#### Jupyter Notebook support + +- Cell magics are now only processed if they are known Python cell magics. Earlier, all + cell magics were tokenized, leading to possible indentation errors e.g. with + `%%writefile`. (#2630) +- Fix assignment to environment variables in Jupyter Notebooks (#2642) + +#### Python 3.10 support + +- Point users to using `--target-version py310` if we detect 3.10-only syntax (#2668) +- Fix `match` statements with open sequence subjects, like `match a, b:` or + `match a, *b:` (#2639) (#2659) +- Fix `match`/`case` statements that contain `match`/`case` soft keywords multiple + times, like `match re.match()` (#2661) +- Fix `case` statements with an inline body (#2665) +- Fix styling of starred expressions inside `match` subject (#2667) +- Fix parser error location on invalid syntax in a `match` statement (#2649) +- Fix Python 3.10 support on platforms without ProcessPoolExecutor (#2631) +- Improve parsing performance on code that uses `match` under `--target-version py310` + up to ~50% (#2670) + +### Packaging + +- Remove dependency on `regex` (#2644) (#2663) + +## 21.11b1 + +### _Black_ + +- Bumped regex version minimum to 2021.4.4 to fix Pattern class usage (#2621) + +## 21.11b0 + +### _Black_ + +- Warn about Python 2 deprecation in more cases by improving Python 2 only syntax + detection (#2592) +- Add experimental PyPy support (#2559) +- Add partial support for the match statement. As it's experimental, it's only enabled + when `--target-version py310` is explicitly specified (#2586) +- Add support for parenthesized with (#2586) +- Declare support for Python 3.10 for running Black (#2562) + +### Integrations + +- Fixed vim plugin with Python 3.10 by removing deprecated distutils import (#2610) +- The vim plugin now parses `skip_magic_trailing_comma` from pyproject.toml (#2613) + +## 21.10b0 + +### _Black_ + +- Document stability policy, that will apply for non-beta releases (#2529) +- Add new `--workers` parameter (#2514) +- Fixed feature detection for positional-only arguments in lambdas (#2532) +- Bumped typed-ast version minimum to 1.4.3 for 3.10 compatibility (#2519) +- Fixed a Python 3.10 compatibility issue where the loop argument was still being passed + even though it has been removed (#2580) +- Deprecate Python 2 formatting support (#2523) + +### _Blackd_ + +- Remove dependency on aiohttp-cors (#2500) +- Bump required aiohttp version to 3.7.4 (#2509) + +### _Black-Primer_ + +- Add primer support for --projects (#2555) +- Print primer summary after individual failures (#2570) + +### Integrations + +- Allow to pass `target_version` in the vim plugin (#1319) +- Install build tools in docker file and use multi-stage build to keep the image size + down (#2582) + +## 21.9b0 + +### Packaging + +- Fix missing modules in self-contained binaries (#2466) +- Fix missing toml extra used during installation (#2475) + +## 21.8b0 + +### _Black_ + +- Add support for formatting Jupyter Notebook files (#2357) +- Move from `appdirs` dependency to `platformdirs` (#2375) +- Present a more user-friendly error if .gitignore is invalid (#2414) +- The failsafe for accidentally added backslashes in f-string expressions has been + hardened to handle more edge cases during quote normalization (#2437) +- Avoid changing a function return type annotation's type to a tuple by adding a + trailing comma (#2384) +- Parsing support has been added for unparenthesized walruses in set literals, set + comprehensions, and indices (#2447). +- Pin `setuptools-scm` build-time dependency version (#2457) +- Exclude typing-extensions version 3.10.0.1 due to it being broken on Python 3.10 + (#2460) + +### _Blackd_ + +- Replace sys.exit(-1) with raise ImportError as it plays more nicely with tools that + scan installed packages (#2440) + +### Integrations + +- The provided pre-commit hooks no longer specify `language_version` to avoid overriding + `default_language_version` (#2430) + +## 21.7b0 + +### _Black_ + +- Configuration files using TOML features higher than spec v0.5.0 are now supported + (#2301) +- Add primer support and test for code piped into black via STDIN (#2315) +- Fix internal error when `FORCE_OPTIONAL_PARENTHESES` feature is enabled (#2332) +- Accept empty stdin (#2346) +- Provide a more useful error when parsing fails during AST safety checks (#2304) + +### Docker + +- Add new `latest_release` tag automation to follow latest black release on docker + images (#2374) + +### Integrations + +- The vim plugin now searches upwards from the directory containing the current buffer + instead of the current working directory for pyproject.toml. (#1871) +- The vim plugin now reads the correct string normalization option in pyproject.toml + (#1869) +- The vim plugin no longer crashes Black when there's boolean values in pyproject.toml + (#1869) + +## 21.6b0 + +### _Black_ + +- Fix failure caused by `fmt: skip` and indentation (#2281) +- Account for += assignment when deciding whether to split string (#2312) +- Correct max string length calculation when there are string operators (#2292) +- Fixed option usage when using the `--code` flag (#2259) +- Do not call `uvloop.install()` when _Black_ is used as a library (#2303) +- Added `--required-version` option to require a specific version to be running (#2300) +- Fix incorrect custom breakpoint indices when string group contains fake f-strings + (#2311) +- Fix regression where `R` prefixes would be lowercased for docstrings (#2285) +- Fix handling of named escapes (`\N{...}`) when `--experimental-string-processing` is + used (#2319) + +### Integrations + +- The official Black action now supports choosing what version to use, and supports the + major 3 OSes. (#1940) + +## 21.5b2 + +### _Black_ + +- A space is no longer inserted into empty docstrings (#2249) +- Fix handling of .gitignore files containing non-ASCII characters on Windows (#2229) +- Respect `.gitignore` files in all levels, not only `root/.gitignore` file (apply + `.gitignore` rules like `git` does) (#2225) +- Restored compatibility with Click 8.0 on Python 3.6 when LANG=C used (#2227) +- Add extra uvloop install + import support if in python env (#2258) +- Fix --experimental-string-processing crash when matching parens are not found (#2283) +- Make sure to split lines that start with a string operator (#2286) +- Fix regular expression that black uses to identify f-expressions (#2287) + +### _Blackd_ + +- Add a lower bound for the `aiohttp-cors` dependency. Only 0.4.0 or higher is + supported. (#2231) + +### Packaging + +- Release self-contained x86_64 MacOS binaries as part of the GitHub release pipeline + (#2198) +- Always build binaries with the latest available Python (#2260) + +### Documentation + +- Add discussion of magic comments to FAQ page (#2272) +- `--experimental-string-processing` will be enabled by default in the future (#2273) +- Fix typos discovered by codespell (#2228) +- Fix Vim plugin installation instructions. (#2235) +- Add new Frequently Asked Questions page (#2247) +- Fix encoding + symlink issues preventing proper build on Windows (#2262) + +## 21.5b1 + +### _Black_ + +- Refactor `src/black/__init__.py` into many files (#2206) + +### Documentation + +- Replaced all remaining references to the + [`master`](https://github.com/psf/black/tree/main) branch with the + [`main`](https://github.com/psf/black/tree/main) branch. Some additional changes in + the source code were also made. (#2210) +- Sigificantly reorganized the documentation to make much more sense. Check them out by + heading over to [the stable docs on RTD](https://black.readthedocs.io/en/stable/). + (#2174) + +## 21.5b0 + +### _Black_ + +- Set `--pyi` mode if `--stdin-filename` ends in `.pyi` (#2169) +- Stop detecting target version as Python 3.9+ with pre-PEP-614 decorators that are + being called but with no arguments (#2182) + +### _Black-Primer_ + +- Add `--no-diff` to black-primer to suppress formatting changes (#2187) + +## 21.4b2 + +### _Black_ + +- Fix crash if the user configuration directory is inaccessible. (#2158) + +- Clarify + [circumstances](https://github.com/psf/black/blob/master/docs/the_black_code_style.md#pragmatism) + in which _Black_ may change the AST (#2159) + +- Allow `.gitignore` rules to be overridden by specifying `exclude` in `pyproject.toml` + or on the command line. (#2170) + +### _Packaging_ + +- Install `primer.json` (used by `black-primer` by default) with black. (#2154) + +## 21.4b1 + +### _Black_ + +- Fix crash on docstrings ending with "\\ ". (#2142) + +- Fix crash when atypical whitespace is cleaned out of dostrings (#2120) + +- Reflect the `--skip-magic-trailing-comma` and `--experimental-string-processing` flags + in the name of the cache file. Without this fix, changes in these flags would not take + effect if the cache had already been populated. (#2131) + +- Don't remove necessary parentheses from assignment expression containing assert / + return statements. (#2143) + +### _Packaging_ + +- Bump pathspec to >= 0.8.1 to solve invalid .gitignore exclusion handling + +## 21.4b0 + +### _Black_ + +- Fixed a rare but annoying formatting instability created by the combination of + optional trailing commas inserted by `Black` and optional parentheses looking at + pre-existing "magic" trailing commas. This fixes issue #1629 and all of its many many + duplicates. (#2126) + +- `Black` now processes one-line docstrings by stripping leading and trailing spaces, + and adding a padding space when needed to break up """". (#1740) + +- `Black` now cleans up leading non-breaking spaces in comments (#2092) + +- `Black` now respects `--skip-string-normalization` when normalizing multiline + docstring quotes (#1637) + +- `Black` no longer removes all empty lines between non-function code and decorators + when formatting typing stubs. Now `Black` enforces a single empty line. (#1646) + +- `Black` no longer adds an incorrect space after a parenthesized assignment expression + in if/while statements (#1655) + +- Added `--skip-magic-trailing-comma` / `-C` to avoid using trailing commas as a reason + to split lines (#1824) + +- fixed a crash when PWD=/ on POSIX (#1631) + +- fixed "I/O operation on closed file" when using --diff (#1664) + +- Prevent coloured diff output being interleaved with multiple files (#1673) + +- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711) + +- Added parsing support for unparenthesized tuples and yield expressions in annotated + assignments (#1835) + +- added `--extend-exclude` argument (PR #2005) + +- speed up caching by avoiding pathlib (#1950) + +- `--diff` correctly indicates when a file doesn't end in a newline (#1662) + +- Added `--stdin-filename` argument to allow stdin to respect `--force-exclude` rules + (#1780) + +- Lines ending with `fmt: skip` will now be not formatted (#1800) + +- PR #2053: Black no longer relies on typed-ast for Python 3.8 and higher + +- PR #2053: Python 2 support is now optional, install with + `python3 -m pip install black[python2]` to maintain support. + +- Exclude `venv` directory by default (#1683) + +- Fixed "Black produced code that is not equivalent to the source" when formatting + Python 2 docstrings (#2037) + +### _Packaging_ + +- Self-contained native _Black_ binaries are now provided for releases via GitHub + Releases (#1743) + +## 20.8b1 + +### _Packaging_ + +- explicitly depend on Click 7.1.2 or newer as `Black` no longer works with versions + older than 7.0 + +## 20.8b0 + +### _Black_ + +- re-implemented support for explicit trailing commas: now it works consistently within + any bracket pair, including nested structures (#1288 and duplicates) + +- `Black` now reindents docstrings when reindenting code around it (#1053) + +- `Black` now shows colored diffs (#1266) + +- `Black` is now packaged using 'py3' tagged wheels (#1388) + +- `Black` now supports Python 3.8 code, e.g. star expressions in return statements + (#1121) + +- `Black` no longer normalizes capital R-string prefixes as those have a + community-accepted meaning (#1244) + +- `Black` now uses exit code 2 when specified configuration file doesn't exit (#1361) + +- `Black` now works on AWS Lambda (#1141) + +- added `--force-exclude` argument (#1032) + +- removed deprecated `--py36` option (#1236) + +- fixed `--diff` output when EOF is encountered (#526) + +- fixed `# fmt: off` handling around decorators (#560) + +- fixed unstable formatting with some `# type: ignore` comments (#1113) + +- fixed invalid removal on organizing brackets followed by indexing (#1575) + +- introduced `black-primer`, a CI tool that allows us to run regression tests against + existing open source users of Black (#1402) + +- introduced property-based fuzzing to our test suite based on Hypothesis and + Hypothersmith (#1566) + +- implemented experimental and disabled by default long string rewrapping (#1132), + hidden under a `--experimental-string-processing` flag while it's being worked on; + this is an undocumented and unsupported feature, you lose Internet points for + depending on it (#1609) + +### Vim plugin + +- prefer virtualenv packages over global packages (#1383) + +## 19.10b0 + +- added support for PEP 572 assignment expressions (#711) + +- added support for PEP 570 positional-only arguments (#943) + +- added support for async generators (#593) + +- added support for pre-splitting collections by putting an explicit trailing comma + inside (#826) + +- added `black -c` as a way to format code passed from the command line (#761) + +- --safe now works with Python 2 code (#840) + +- fixed grammar selection for Python 2-specific code (#765) + +- fixed feature detection for trailing commas in function definitions and call sites + (#763) + +- `# fmt: off`/`# fmt: on` comment pairs placed multiple times within the same block of + code now behave correctly (#1005) + +- _Black_ no longer crashes on Windows machines with more than 61 cores (#838) + +- _Black_ no longer crashes on standalone comments prepended with a backslash (#767) + +- _Black_ no longer crashes on `from` ... `import` blocks with comments (#829) + +- _Black_ no longer crashes on Python 3.7 on some platform configurations (#494) + +- _Black_ no longer fails on comments in from-imports (#671) + +- _Black_ no longer fails when the file starts with a backslash (#922) + +- _Black_ no longer merges regular comments with type comments (#1027) + +- _Black_ no longer splits long lines that contain type comments (#997) + +- removed unnecessary parentheses around `yield` expressions (#834) + +- added parentheses around long tuples in unpacking assignments (#832) + +- added parentheses around complex powers when they are prefixed by a unary operator + (#646) + +- fixed bug that led _Black_ format some code with a line length target of 1 (#762) + +- _Black_ no longer introduces quotes in f-string subexpressions on string boundaries + (#863) + +- if _Black_ puts parenthesis around a single expression, it moves comments to the + wrapped expression instead of after the brackets (#872) + +- `blackd` now returns the version of _Black_ in the response headers (#1013) + +- `blackd` can now output the diff of formats on source code when the `X-Diff` header is + provided (#969) + +## 19.3b0 + +- new option `--target-version` to control which Python versions _Black_-formatted code + should target (#618) + +- deprecated `--py36` (use `--target-version=py36` instead) (#724) + +- _Black_ no longer normalizes numeric literals to include `_` separators (#696) + +- long `del` statements are now split into multiple lines (#698) + +- type comments are no longer mangled in function signatures + +- improved performance of formatting deeply nested data structures (#509) + +- _Black_ now properly formats multiple files in parallel on Windows (#632) + +- _Black_ now creates cache files atomically which allows it to be used in parallel + pipelines (like `xargs -P8`) (#673) + +- _Black_ now correctly indents comments in files that were previously formatted with + tabs (#262) + +- `blackd` now supports CORS (#622) + +## 18.9b0 + +- numeric literals are now formatted by _Black_ (#452, #461, #464, #469): + + - numeric literals are normalized to include `_` separators on Python 3.6+ code + + - added `--skip-numeric-underscore-normalization` to disable the above behavior and + leave numeric underscores as they were in the input + + - code with `_` in numeric literals is recognized as Python 3.6+ + + - most letters in numeric literals are lowercased (e.g., in `1e10`, `0x01`) + + - hexadecimal digits are always uppercased (e.g. `0xBADC0DE`) + +- added `blackd`, see + [its documentation](https://github.com/psf/black/blob/18.9b0/README.md#blackd) for + more info (#349) + +- adjacent string literals are now correctly split into multiple lines (#463) + +- trailing comma is now added to single imports that don't fit on a line (#250) + +- cache is now populated when `--check` is successful for a file which speeds up + consecutive checks of properly formatted unmodified files (#448) + +- whitespace at the beginning of the file is now removed (#399) + +- fixed mangling [pweave](http://mpastell.com/pweave/) and + [Spyder IDE](https://www.spyder-ide.org/) special comments (#532) + +- fixed unstable formatting when unpacking big tuples (#267) + +- fixed parsing of `__future__` imports with renames (#389) + +- fixed scope of `# fmt: off` when directly preceding `yield` and other nodes (#385) + +- fixed formatting of lambda expressions with default arguments (#468) + +- fixed `async for` statements: _Black_ no longer breaks them into separate lines (#372) + +- note: the Vim plugin stopped registering `,=` as a default chord as it turned out to + be a bad idea (#415) + +## 18.6b4 + +- hotfix: don't freeze when multiple comments directly precede `# fmt: off` (#371) + +## 18.6b3 + +- typing stub files (`.pyi`) now have blank lines added after constants (#340) + +- `# fmt: off` and `# fmt: on` are now much more dependable: + + - they now work also within bracket pairs (#329) + + - they now correctly work across function/class boundaries (#335) + + - they now work when an indentation block starts with empty lines or misaligned + comments (#334) + +- made Click not fail on invalid environments; note that Click is right but the + likelihood we'll need to access non-ASCII file paths when dealing with Python source + code is low (#277) + +- fixed improper formatting of f-strings with quotes inside interpolated expressions + (#322) + +- fixed unnecessary slowdown when long list literals where found in a file + +- fixed unnecessary slowdown on AST nodes with very many siblings + +- fixed cannibalizing backslashes during string normalization + +- fixed a crash due to symbolic links pointing outside of the project directory (#338) + +## 18.6b2 + +- added `--config` (#65) + +- added `-h` equivalent to `--help` (#316) + +- fixed improper unmodified file caching when `-S` was used + +- fixed extra space in string unpacking (#305) + +- fixed formatting of empty triple quoted strings (#313) + +- fixed unnecessary slowdown in comment placement calculation on lines without comments + +## 18.6b1 + +- hotfix: don't output human-facing information on stdout (#299) + +- hotfix: don't output cake emoji on non-zero return code (#300) + +## 18.6b0 + +- added `--include` and `--exclude` (#270) + +- added `--skip-string-normalization` (#118) + +- added `--verbose` (#283) + +- the header output in `--diff` now actually conforms to the unified diff spec + +- fixed long trivial assignments being wrapped in unnecessary parentheses (#273) + +- fixed unnecessary parentheses when a line contained multiline strings (#232) + +- fixed stdin handling not working correctly if an old version of Click was used (#276) + +- _Black_ now preserves line endings when formatting a file in place (#258) + +## 18.5b1 + +- added `--pyi` (#249) + +- added `--py36` (#249) + +- Python grammar pickle caches are stored with the formatting caches, making _Black_ + work in environments where site-packages is not user-writable (#192) + +- _Black_ now enforces a PEP 257 empty line after a class-level docstring (and/or + fields) and the first method + +- fixed invalid code produced when standalone comments were present in a trailer that + was omitted from line splitting on a large expression (#237) + +- fixed optional parentheses being removed within `# fmt: off` sections (#224) + +- fixed invalid code produced when stars in very long imports were incorrectly wrapped + in optional parentheses (#234) + +- fixed unstable formatting when inline comments were moved around in a trailer that was + omitted from line splitting on a large expression (#238) + +- fixed extra empty line between a class declaration and the first method if no class + docstring or fields are present (#219) + +- fixed extra empty line between a function signature and an inner function or inner + class (#196) + +## 18.5b0 + +- call chains are now formatted according to the + [fluent interfaces](https://en.wikipedia.org/wiki/Fluent_interface) style (#67) + +- data structure literals (tuples, lists, dictionaries, and sets) are now also always + exploded like imports when they don't fit in a single line (#152) + +- slices are now formatted according to PEP 8 (#178) + +- parentheses are now also managed automatically on the right-hand side of assignments + and return statements (#140) + +- math operators now use their respective priorities for delimiting multiline + expressions (#148) + +- optional parentheses are now omitted on expressions that start or end with a bracket + and only contain a single operator (#177) + +- empty parentheses in a class definition are now removed (#145, #180) + +- string prefixes are now standardized to lowercase and `u` is removed on Python 3.6+ + only code and Python 2.7+ code with the `unicode_literals` future import (#188, #198, + #199) + +- typing stub files (`.pyi`) are now formatted in a style that is consistent with PEP + 484 (#207, #210) + +- progress when reformatting many files is now reported incrementally + +- fixed trailers (content with brackets) being unnecessarily exploded into their own + lines after a dedented closing bracket (#119) + +- fixed an invalid trailing comma sometimes left in imports (#185) + +- fixed non-deterministic formatting when multiple pairs of removable parentheses were + used (#183) + +- fixed multiline strings being unnecessarily wrapped in optional parentheses in long + assignments (#215) + +- fixed not splitting long from-imports with only a single name + +- fixed Python 3.6+ file discovery by also looking at function calls with unpacking. + This fixed non-deterministic formatting if trailing commas where used both in function + signatures with stars and function calls with stars but the former would be + reformatted to a single line. + +- fixed crash on dealing with optional parentheses (#193) + +- fixed "is", "is not", "in", and "not in" not considered operators for splitting + purposes + +- fixed crash when dead symlinks where encountered + +## 18.4a4 + +- don't populate the cache on `--check` (#175) + +## 18.4a3 + +- added a "cache"; files already reformatted that haven't changed on disk won't be + reformatted again (#109) + +- `--check` and `--diff` are no longer mutually exclusive (#149) + +- generalized star expression handling, including double stars; this fixes + multiplication making expressions "unsafe" for trailing commas (#132) + +- _Black_ no longer enforces putting empty lines behind control flow statements (#90) + +- _Black_ now splits imports like "Mode 3 + trailing comma" of isort (#127) + +- fixed comment indentation when a standalone comment closes a block (#16, #32) + +- fixed standalone comments receiving extra empty lines if immediately preceding a + class, def, or decorator (#56, #154) + +- fixed `--diff` not showing entire path (#130) + +- fixed parsing of complex expressions after star and double stars in function calls + (#2) + +- fixed invalid splitting on comma in lambda arguments (#133) + +- fixed missing splits of ternary expressions (#141) + +## 18.4a2 + +- fixed parsing of unaligned standalone comments (#99, #112) + +- fixed placement of dictionary unpacking inside dictionary literals (#111) + +- Vim plugin now works on Windows, too + +- fixed unstable formatting when encountering unnecessarily escaped quotes in a string + (#120) + +## 18.4a1 + +- added `--quiet` (#78) + +- added automatic parentheses management (#4) + +- added [pre-commit](https://pre-commit.com) integration (#103, #104) + +- fixed reporting on `--check` with multiple files (#101, #102) + +- fixed removing backslash escapes from raw strings (#100, #105) + +## 18.4a0 + +- added `--diff` (#87) + +- add line breaks before all delimiters, except in cases like commas, to better comply + with PEP 8 (#73) + +- standardize string literals to use double quotes (almost) everywhere (#75) + +- fixed handling of standalone comments within nested bracketed expressions; _Black_ + will no longer produce super long lines or put all standalone comments at the end of + the expression (#22) + +- fixed 18.3a4 regression: don't crash and burn on empty lines with trailing whitespace + (#80) + +- fixed 18.3a4 regression: `# yapf: disable` usage as trailing comment would cause + _Black_ to not emit the rest of the file (#95) + +- when CTRL+C is pressed while formatting many files, _Black_ no longer freaks out with + a flurry of asyncio-related exceptions + +- only allow up to two empty lines on module level and only single empty lines within + functions (#74) + +## 18.3a4 + +- `# fmt: off` and `# fmt: on` are implemented (#5) + +- automatic detection of deprecated Python 2 forms of print statements and exec + statements in the formatted file (#49) + +- use proper spaces for complex expressions in default values of typed function + arguments (#60) + +- only return exit code 1 when --check is used (#50) + +- don't remove single trailing commas from square bracket indexing (#59) + +- don't omit whitespace if the previous factor leaf wasn't a math operator (#55) + +- omit extra space in kwarg unpacking if it's the first argument (#46) + +- omit extra space in + [Sphinx auto-attribute comments](http://www.sphinx-doc.org/en/stable/ext/autodoc.html#directive-autoattribute) + (#68) + +## 18.3a3 + +- don't remove single empty lines outside of bracketed expressions (#19) + +- added ability to pipe formatting from stdin to stdin (#25) + +- restored ability to format code with legacy usage of `async` as a name (#20, #42) + +- even better handling of numpy-style array indexing (#33, again) + +## 18.3a2 + +- changed positioning of binary operators to occur at beginning of lines instead of at + the end, following + [a recent change to PEP 8](https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b) + (#21) + +- ignore empty bracket pairs while splitting. This avoids very weirdly looking + formattings (#34, #35) + +- remove a trailing comma if there is a single argument to a call + +- if top level functions were separated by a comment, don't put four empty lines after + the upper function + +- fixed unstable formatting of newlines with imports + +- fixed unintentional folding of post scriptum standalone comments into last statement + if it was a simple statement (#18, #28) + +- fixed missing space in numpy-style array indexing (#33) + +- fixed spurious space after star-based unary expressions (#31) + +## 18.3a1 + +- added `--check` + +- only put trailing commas in function signatures and calls if it's safe to do so. If + the file is Python 3.6+ it's always safe, otherwise only safe if there are no `*args` + or `**kwargs` used in the signature or call. (#8) + +- fixed invalid spacing of dots in relative imports (#6, #13) + +- fixed invalid splitting after comma on unpacked variables in for-loops (#23) + +- fixed spurious space in parenthesized set expressions (#7) + +- fixed spurious space after opening parentheses and in default arguments (#14, #17) + +- fixed spurious space after unary operators when the operand was a complex expression + (#15) + +## 18.3a0 + +- first published version, Happy 🍰 Day 2018! + +- alpha quality + +- date-versioned (see: ) diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/RECORD new file mode 100644 index 00000000..8dc945cc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/RECORD @@ -0,0 +1,115 @@ +../../../bin/black,sha256=ggfgPyn_h81r1xTQ012MSxX8NcORQDVYvKztN2UJNEE,283 +../../../bin/blackd,sha256=W1rXuPgZQ8C1LSDpRh7Rnn916KEsfpd58-0egZsCvc8,284 +6b397dd64e00b5aff23d__mypyc.cpython-311-x86_64-linux-gnu.so,sha256=GQNLD6JN-qViU3ck3iGRMCewCjbO2k9ehTncOh4tK70,3993856 +__pycache__/_black_version.cpython-311.pyc,, +_black_version.py,sha256=u3_onRL-3_Ow8zYRCCNEH6AuMORqTwvKRYCtQKLUBfU,19 +black-23.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +black-23.1.0.dist-info/METADATA,sha256=-pB22iz9v6UAIzfgjxUSvVLpx6uiuYIsqBh3EFD9g7c,58959 +black-23.1.0.dist-info/RECORD,, +black-23.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +black-23.1.0.dist-info/WHEEL,sha256=EH5KzJshMQk4ddij7sGfE9Nvw8pfffB7c61DapoFw10,148 +black-23.1.0.dist-info/entry_points.txt,sha256=qBIyywHwGRkJj7kieq86kqf77rz3qGC4Joj36lHnxwc,78 +black-23.1.0.dist-info/licenses/AUTHORS.md,sha256=cpHlH2nYyIXWEnA0LccoGenN-g5HZ7341klZ7MwbdIU,8043 +black-23.1.0.dist-info/licenses/LICENSE,sha256=nAQo8MO0d5hQz1vZbhGqqK_HLUqG1KNiI9erouWNbgA,1080 +black/__init__.cpython-311-x86_64-linux-gnu.so,sha256=VhyeZgW4aZsL5lCmCJxMOjtNe70NGL8wNcdVnc1fADE,8248 +black/__init__.py,sha256=VWazgq80l38r1Cn5Ea7fH5bMuDdFuQJtYgBKGWJganI,46682 +black/__main__.py,sha256=mogeA4o9zt4w-ufKvaQjSEhtSgQkcMVLK9ChvdB5wH8,47 +black/__pycache__/__init__.cpython-311.pyc,, +black/__pycache__/__main__.cpython-311.pyc,, +black/__pycache__/brackets.cpython-311.pyc,, +black/__pycache__/cache.cpython-311.pyc,, +black/__pycache__/comments.cpython-311.pyc,, +black/__pycache__/concurrency.cpython-311.pyc,, +black/__pycache__/const.cpython-311.pyc,, +black/__pycache__/debug.cpython-311.pyc,, +black/__pycache__/files.cpython-311.pyc,, +black/__pycache__/handle_ipynb_magics.cpython-311.pyc,, +black/__pycache__/linegen.cpython-311.pyc,, +black/__pycache__/lines.cpython-311.pyc,, +black/__pycache__/mode.cpython-311.pyc,, +black/__pycache__/nodes.cpython-311.pyc,, +black/__pycache__/numerics.cpython-311.pyc,, +black/__pycache__/output.cpython-311.pyc,, +black/__pycache__/parsing.cpython-311.pyc,, +black/__pycache__/report.cpython-311.pyc,, +black/__pycache__/rusty.cpython-311.pyc,, +black/__pycache__/strings.cpython-311.pyc,, +black/__pycache__/trans.cpython-311.pyc,, +black/brackets.cpython-311-x86_64-linux-gnu.so,sha256=VDIeD54I8rOJXiaRRpNd0ypRzG-oOgkYugPhOPGZrlQ,8256 +black/brackets.py,sha256=UH8o6FxSbum5PyPWWIPJoXYnSL3ssu39IXzuFPZRSxw,12273 +black/cache.cpython-311-x86_64-linux-gnu.so,sha256=vCqfylt7IcLLhvGWrLGfH57yMV__83mcwFgs2WaNvqs,8256 +black/cache.py,sha256=yC8aCiOGpNq2kqsrtSUCjjH6NmTXMkUKg9gO9w4pUEg,2946 +black/comments.cpython-311-x86_64-linux-gnu.so,sha256=tWJF4rkTCxu0JGXsROV3g0Zp4PhekMuEVMkhbmfxfdw,8256 +black/comments.py,sha256=AMfuoZq4ISyf8l6AUJtKAM4Y1RknjpVwXQ3xONqUFLA,12720 +black/concurrency.py,sha256=8X2MKLFDPYNIV4Fca9065e6f9pNeORjvFRxKP14ym8k,6286 +black/const.cpython-311-x86_64-linux-gnu.so,sha256=rB9yo4zdcgqWBd9HJEwx656MgRUxGOpwNzUXi2B-nfg,8256 +black/const.py,sha256=4_6Bjr4eLrEEGY8uX_ZgR1Y90f3ok21RGlgDFgwGfjQ,284 +black/debug.py,sha256=PDBJeevHiMPzdQTZmC4kCZbQxyBXOSzQvIVnrFCXALU,1594 +black/files.py,sha256=W-V0evMTr3KAd4ZWK1hd-rmAzF2NrggiCTTqCQT7iB4,13448 +black/handle_ipynb_magics.cpython-311-x86_64-linux-gnu.so,sha256=1SXeBdZ3TniAe8zdaE2oOi9I3EqiHeVrvfpXxfNCkfs,8280 +black/handle_ipynb_magics.py,sha256=bH0rwjyECd-phroNfrrQIXOvergQjTzcU6rFMkhdNKU,13501 +black/linegen.cpython-311-x86_64-linux-gnu.so,sha256=7qxMC3ygMNbGnwMwKLANStvQ604Zm_7CN_SxCfZXk8Q,8256 +black/linegen.py,sha256=sKWAhW57fpgIoJYKl7fDPr6jx8h8jtxvv0OXqTTUzyc,59117 +black/lines.cpython-311-x86_64-linux-gnu.so,sha256=tok8uP3SJY_R72m-dT1GIlokH53M9xan_QoyhfeCKKU,8256 +black/lines.py,sha256=yjcTQiyh_3ieNXTGYRTkjIEx69nYlfaUgAnUPqwTNQs,30834 +black/mode.cpython-311-x86_64-linux-gnu.so,sha256=uyxFZfbkIq9wZLtCk-n-Eabvu2VbTmPhjC-KVhOC5Uk,8248 +black/mode.py,sha256=SsuB0FummtWMtWT3OybqcPdrXUMBDYOLVQIoHIxzi-4,7049 +black/nodes.cpython-311-x86_64-linux-gnu.so,sha256=5mjT2mgQ8T2Frin9CVZ7xwfWQz6IukSFXebulW-5jlc,8256 +black/nodes.py,sha256=OEL_CByeL3iy9KVB7BtAqFKbMKm90HfvErBFCNUMuEQ,24864 +black/numerics.cpython-311-x86_64-linux-gnu.so,sha256=0TM-5uncg3qK9ys0J27emPOQBjw7XYUrZFJFl3lG__k,8256 +black/numerics.py,sha256=TBID6blEBXZgGIigMXS5JpX2-Trv9lK3WoWPkkHrpac,1653 +black/output.py,sha256=qfdOuT8z5WSm_GxwP24X5XrjrxPacjj1k1gC40crJGw,3486 +black/parsing.cpython-311-x86_64-linux-gnu.so,sha256=9sM2ef97tEJl3vINRdvkj1xnlkZyvVMlr6qRQxgW_ss,8256 +black/parsing.py,sha256=N5UdkgpgMnKQOBMFfAjd7y7RFyrQuh4A2PpajTFTH04,9792 +black/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +black/report.py,sha256=vcZXcQwoT2LPYD0AoL7w3tshKw_i5DjxGRsqqLZQksc,3451 +black/rusty.cpython-311-x86_64-linux-gnu.so,sha256=Xs9ZgjZgaNlXx_uxk2ejkVsdU9MCi4B05vOqBb8Tw_o,8256 +black/rusty.py,sha256=kmKIJpD9J0bPfkQ67Sy8HkVcx-5CF02acggiaaxM7sc,556 +black/strings.cpython-311-x86_64-linux-gnu.so,sha256=jsLtPDpdvT0VHFprlQHlv_OMWvy4wNSuPQhd9nyFtTs,8256 +black/strings.py,sha256=RHzuLjU3elJfT8DbIJ3Nmb1qp8qvMILyKb40YvU-bhc,9401 +black/trans.cpython-311-x86_64-linux-gnu.so,sha256=KDMtZkMFw6uaR7rylaj4ZNVacP7NQh5VnUQeKkFv664,8256 +black/trans.py,sha256=m61WQDDbppBNVUbCVjXRPUn7uisH0qAOoosoNnxAFAI,90097 +blackd/__init__.py,sha256=cTJotjnER9YB0rDWo3hOWSg4v0mNAx5DFmCJdyB1s5k,8068 +blackd/__main__.py,sha256=L4xAcDh1K5zb6SsJB102AewW2G13P9-w2RiEwuFj8WA,37 +blackd/__pycache__/__init__.cpython-311.pyc,, +blackd/__pycache__/__main__.cpython-311.pyc,, +blackd/__pycache__/middlewares.cpython-311.pyc,, +blackd/middlewares.py,sha256=QS7cs86Ojuaqh64dGneimhJ-f30rDI646c27ts4Dwh0,1585 +blib2to3/Grammar.txt,sha256=uIxG7IrdemRi94OtTUaVoW2ktYyKyuXHluWFMMq8Af4,11278 +blib2to3/LICENSE,sha256=V4mIG4rrnJH1g19bt8q-hKD-zUuyvi9UyeaVenjseZ0,12762 +blib2to3/PatternGrammar.txt,sha256=7lul2ztnIqDi--JWDrwciD5yMo75w7TaHHxdHMZJvOM,793 +blib2to3/README,sha256=C_K4Os-RntLrCpCHOKr43cnhQAaG94psjrgOX6tywg8,1071 +blib2to3/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 +blib2to3/__pycache__/__init__.cpython-311.pyc,, +blib2to3/__pycache__/pygram.cpython-311.pyc,, +blib2to3/__pycache__/pytree.cpython-311.pyc,, +blib2to3/pgen2/__init__.py,sha256=hY6w9QUzvTvRb-MoFfd_q_7ZLt6IUHC2yxWCfsZupQA,143 +blib2to3/pgen2/__pycache__/__init__.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/conv.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/driver.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/grammar.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/literals.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/parse.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/pgen.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/token.cpython-311.pyc,, +blib2to3/pgen2/__pycache__/tokenize.cpython-311.pyc,, +blib2to3/pgen2/conv.cpython-311-x86_64-linux-gnu.so,sha256=c2ryGboE-0KMiIUHysr_Mqsnyl6JrFaZzawXEKd471U,8256 +blib2to3/pgen2/conv.py,sha256=wGS0vapMV81PvxisAentOrXCZX1IlCFulQDW5Ha9Ueo,9607 +blib2to3/pgen2/driver.cpython-311-x86_64-linux-gnu.so,sha256=tnJiG4sC4UwJ89mrGGPcm8CtIyb5stTMRklw3N4Bt3E,8264 +blib2to3/pgen2/driver.py,sha256=A5HNBnHrl1amhullooyqWEtU2jOnByIketrdbb5zMmY,10670 +blib2to3/pgen2/grammar.cpython-311-x86_64-linux-gnu.so,sha256=Oyjz5o-jW3qeN5U6gDD33fTVpV4_y7HStZVtvkH9DEo,8264 +blib2to3/pgen2/grammar.py,sha256=EJW7w2JIZtzi_g1JWAmONf4kCH8KJD6GVn_1R1uFnDA,6873 +blib2to3/pgen2/literals.cpython-311-x86_64-linux-gnu.so,sha256=zgnZv6KWFT6VVqk3xM-vneNiVrhOoeAiQxXi--AlfJM,8264 +blib2to3/pgen2/literals.py,sha256=vbzw1RX0NvATBR971WwRNtp68fsXky4-pV4cBZ02_9E,1628 +blib2to3/pgen2/parse.cpython-311-x86_64-linux-gnu.so,sha256=zpnGRBSOVdOjPjjdGAmMl9OSbxPLUmD_qyR-J2J7NsY,8264 +blib2to3/pgen2/parse.py,sha256=QJhDkRCgEtc6jQterhuNwvtURIzuz_8Forq5X5k4mQo,14853 +blib2to3/pgen2/pgen.cpython-311-x86_64-linux-gnu.so,sha256=g0kyrkxLEXNyjogA-nKQ8m0P_L2ttTYpWi8EM9AhMQI,8256 +blib2to3/pgen2/pgen.py,sha256=8gkqFARDLVp8tdpjxjJEP1YytCZvNrLl5tuK6SdM_nc,15491 +blib2to3/pgen2/token.cpython-311-x86_64-linux-gnu.so,sha256=tQ_FsW2qRvbryNlsWRRix5hcQ_IRzgaaQXQ4RcGhrBg,8264 +blib2to3/pgen2/token.py,sha256=eDZWhONvSUNGsWYzHhgBd1pIQ2HlVPP7aon0S1CLuKE,1919 +blib2to3/pgen2/tokenize.cpython-311-x86_64-linux-gnu.so,sha256=7JNHlbvrChrieSUoWpTIvZDvM7w4X_tYZzqhEILCp9c,8264 +blib2to3/pgen2/tokenize.py,sha256=CT6TXaY9RJkboV9NJcxQf7KZo07KpxsjOSY4gymGDD4,22842 +blib2to3/pygram.cpython-311-x86_64-linux-gnu.so,sha256=s65ZatQq-qDnR9plai06dKASkYob4p10SM7oL5W2UjQ,8256 +blib2to3/pygram.py,sha256=uCU5Dyc1pObqP1TOJ9IjOkpVqaVpsg-nRovAJt7xOrU,5732 +blib2to3/pytree.cpython-311-x86_64-linux-gnu.so,sha256=EVgnZXshf81mfqkX_aChd8P8YiqT49I7l9sC0mPnyWY,8256 +blib2to3/pytree.py,sha256=We4PqLlpIOT2-625It5HN9ToD8a1OHrl5_FYmDE_0v4,32234 diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/WHEEL new file mode 100644 index 00000000..3f380b57 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.12.2 +Root-Is-Purelib: false +Tag: cp311-cp311-manylinux_2_17_x86_64 +Tag: cp311-cp311-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/entry_points.txt b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/entry_points.txt new file mode 100644 index 00000000..d0bf9079 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +black = black:patched_main +blackd = blackd:patched_main [d] diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/AUTHORS.md b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/AUTHORS.md new file mode 100644 index 00000000..ab3f30b8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/AUTHORS.md @@ -0,0 +1,195 @@ +# Authors + +Glued together by [Łukasz Langa](mailto:lukasz@langa.pl). + +Maintained with: + +- [Carol Willing](mailto:carolcode@willingconsulting.com) +- [Carl Meyer](mailto:carl@oddbird.net) +- [Jelle Zijlstra](mailto:jelle.zijlstra@gmail.com) +- [Mika Naylor](mailto:mail@autophagy.io) +- [Zsolt Dollenstein](mailto:zsol.zsol@gmail.com) +- [Cooper Lees](mailto:me@cooperlees.com) +- [Richard Si](mailto:sichard26@gmail.com) +- [Felix Hildén](mailto:felix.hilden@gmail.com) +- [Batuhan Taskaya](mailto:batuhan@python.org) + +Multiple contributions by: + +- [Abdur-Rahmaan Janhangeer](mailto:arj.python@gmail.com) +- [Adam Johnson](mailto:me@adamj.eu) +- [Adam Williamson](mailto:adamw@happyassassin.net) +- [Alexander Huynh](mailto:ahrex-gh-psf-black@e.sc) +- [Alexandr Artemyev](mailto:mogost@gmail.com) +- [Alex Vandiver](mailto:github@chmrr.net) +- [Allan Simon](mailto:allan.simon@supinfo.com) +- Anders-Petter Ljungquist +- [Amethyst Reese](mailto:amy@n7.gg) +- [Andrew Thorp](mailto:andrew.thorp.dev@gmail.com) +- [Andrew Zhou](mailto:andrewfzhou@gmail.com) +- [Andrey](mailto:dyuuus@yandex.ru) +- [Andy Freeland](mailto:andy@andyfreeland.net) +- [Anthony Sottile](mailto:asottile@umich.edu) +- [Antonio Ossa Guerra](mailto:aaossa+black@uc.cl) +- [Arjaan Buijk](mailto:arjaan.buijk@gmail.com) +- [Arnav Borbornah](mailto:arnavborborah11@gmail.com) +- [Artem Malyshev](mailto:proofit404@gmail.com) +- [Asger Hautop Drewsen](mailto:asgerdrewsen@gmail.com) +- [Augie Fackler](mailto:raf@durin42.com) +- [Aviskar KC](mailto:aviskarkc10@gmail.com) +- Batuhan Taşkaya +- [Benjamin Wohlwend](mailto:bw@piquadrat.ch) +- [Benjamin Woodruff](mailto:github@benjam.info) +- [Bharat Raghunathan](mailto:bharatraghunthan9767@gmail.com) +- [Brandt Bucher](mailto:brandtbucher@gmail.com) +- [Brett Cannon](mailto:brett@python.org) +- [Bryan Bugyi](mailto:bryan.bugyi@rutgers.edu) +- [Bryan Forbes](mailto:bryan@reigndropsfall.net) +- [Calum Lind](mailto:calumlind@gmail.com) +- [Charles](mailto:peacech@gmail.com) +- Charles Reid +- [Christian Clauss](mailto:cclauss@bluewin.ch) +- [Christian Heimes](mailto:christian@python.org) +- [Chuck Wooters](mailto:chuck.wooters@microsoft.com) +- [Chris Rose](mailto:offline@offby1.net) +- Codey Oxley +- [Cong](mailto:congusbongus@gmail.com) +- [Cooper Ry Lees](mailto:me@cooperlees.com) +- [Dan Davison](mailto:dandavison7@gmail.com) +- [Daniel Hahler](mailto:github@thequod.de) +- [Daniel M. Capella](mailto:polycitizen@gmail.com) +- Daniele Esposti +- [David Hotham](mailto:david.hotham@metaswitch.com) +- [David Lukes](mailto:dafydd.lukes@gmail.com) +- [David Szotten](mailto:davidszotten@gmail.com) +- [Denis Laxalde](mailto:denis@laxalde.org) +- [Douglas Thor](mailto:dthor@transphormusa.com) +- dylanjblack +- [Eli Treuherz](mailto:eli@treuherz.com) +- [Emil Hessman](mailto:emil@hessman.se) +- [Felix Kohlgrüber](mailto:felix.kohlgrueber@gmail.com) +- [Florent Thiery](mailto:fthiery@gmail.com) +- Francisco +- [Giacomo Tagliabue](mailto:giacomo.tag@gmail.com) +- [Greg Gandenberger](mailto:ggandenberger@shoprunner.com) +- [Gregory P. Smith](mailto:greg@krypto.org) +- Gustavo Camargo +- hauntsaninja +- [Hadi Alqattan](mailto:alqattanhadizaki@gmail.com) +- [Hassan Abouelela](mailto:hassan@hassanamr.com) +- [Heaford](mailto:dan@heaford.com) +- [Hugo Barrera](mailto::hugo@barrera.io) +- Hugo van Kemenade +- [Hynek Schlawack](mailto:hs@ox.cx) +- [Ionite](mailto:dev@ionite.io) +- [Ivan Katanić](mailto:ivan.katanic@gmail.com) +- [Jakub Kadlubiec](mailto:jakub.kadlubiec@skyscanner.net) +- [Jakub Warczarek](mailto:jakub.warczarek@gmail.com) +- [Jan Hnátek](mailto:jan.hnatek@gmail.com) +- [Jason Fried](mailto:me@jasonfried.info) +- [Jason Friedland](mailto:jason@friedland.id.au) +- [jgirardet](mailto:ijkl@netc.fr) +- Jim Brännlund +- [Jimmy Jia](mailto:tesrin@gmail.com) +- [Joe Antonakakis](mailto:jma353@cornell.edu) +- [Jon Dufresne](mailto:jon.dufresne@gmail.com) +- [Jonas Obrist](mailto:ojiidotch@gmail.com) +- [Jonty Wareing](mailto:jonty@jonty.co.uk) +- [Jose Nazario](mailto:jose.monkey.org@gmail.com) +- [Joseph Larson](mailto:larson.joseph@gmail.com) +- [Josh Bode](mailto:joshbode@fastmail.com) +- [Josh Holland](mailto:anowlcalledjosh@gmail.com) +- [Joshua Cannon](mailto:joshdcannon@gmail.com) +- [José Padilla](mailto:jpadilla@webapplicate.com) +- [Juan Luis Cano Rodríguez](mailto:hello@juanlu.space) +- [kaiix](mailto:kvn.hou@gmail.com) +- [Katie McLaughlin](mailto:katie@glasnt.com) +- Katrin Leinweber +- [Keith Smiley](mailto:keithbsmiley@gmail.com) +- [Kenyon Ralph](mailto:kenyon@kenyonralph.com) +- [Kevin Kirsche](mailto:Kev.Kirsche+GitHub@gmail.com) +- [Kyle Hausmann](mailto:kyle.hausmann@gmail.com) +- [Kyle Sunden](mailto:sunden@wisc.edu) +- Lawrence Chan +- [Linus Groh](mailto:mail@linusgroh.de) +- [Loren Carvalho](mailto:comradeloren@gmail.com) +- [Luka Sterbic](mailto:luka.sterbic@gmail.com) +- [LukasDrude](mailto:mail@lukas-drude.de) +- Mahmoud Hossam +- Mariatta +- [Matt VanEseltine](mailto:vaneseltine@gmail.com) +- [Matthew Clapp](mailto:itsayellow+dev@gmail.com) +- [Matthew Walster](mailto:matthew@walster.org) +- Max Smolens +- [Michael Aquilina](mailto:michaelaquilina@gmail.com) +- [Michael Flaxman](mailto:michael.flaxman@gmail.com) +- [Michael J. Sullivan](mailto:sully@msully.net) +- [Michael McClimon](mailto:michael@mcclimon.org) +- [Miguel Gaiowski](mailto:miggaiowski@gmail.com) +- [Mike](mailto:roshi@fedoraproject.org) +- [mikehoyio](mailto:mikehoy@gmail.com) +- [Min ho Kim](mailto:minho42@gmail.com) +- [Miroslav Shubernetskiy](mailto:miroslav@miki725.com) +- MomIsBestFriend +- [Nathan Goldbaum](mailto:ngoldbau@illinois.edu) +- [Nathan Hunt](mailto:neighthan.hunt@gmail.com) +- [Neraste](mailto:neraste.herr10@gmail.com) +- [Nikolaus Waxweiler](mailto:madigens@gmail.com) +- [Ofek Lev](mailto:ofekmeister@gmail.com) +- [Osaetin Daniel](mailto:osaetindaniel@gmail.com) +- [otstrel](mailto:otstrel@gmail.com) +- [Pablo Galindo](mailto:Pablogsal@gmail.com) +- [Paul Ganssle](mailto:p.ganssle@gmail.com) +- [Paul Meinhardt](mailto:mnhrdt@gmail.com) +- [Peter Bengtsson](mailto:mail@peterbe.com) +- [Peter Grayson](mailto:pete@jpgrayson.net) +- [Peter Stensmyr](mailto:peter.stensmyr@gmail.com) +- pmacosta +- [Quentin Pradet](mailto:quentin@pradet.me) +- [Ralf Schmitt](mailto:ralf@systemexit.de) +- [Ramón Valles](mailto:mroutis@protonmail.com) +- [Richard Fearn](mailto:richardfearn@gmail.com) +- [Rishikesh Jha](mailto:rishijha424@gmail.com) +- [Rupert Bedford](mailto:rupert@rupertb.com) +- Russell Davis +- [Sagi Shadur](mailto:saroad2@gmail.com) +- [Rémi Verschelde](mailto:rverschelde@gmail.com) +- [Sami Salonen](mailto:sakki@iki.fi) +- [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com) +- [Sanket Dasgupta](mailto:sanketdasgupta@gmail.com) +- Sergi +- [Scott Stevenson](mailto:scott@stevenson.io) +- Shantanu +- [shaoran](mailto:shaoran@sakuranohana.org) +- [Shinya Fujino](mailto:shf0811@gmail.com) +- springstan +- [Stavros Korokithakis](mailto:hi@stavros.io) +- [Stephen Rosen](mailto:sirosen@globus.org) +- [Steven M. Vascellaro](mailto:S.Vascellaro@gmail.com) +- [Sunil Kapil](mailto:snlkapil@gmail.com) +- [Sébastien Eustace](mailto:sebastien.eustace@gmail.com) +- [Tal Amuyal](mailto:TalAmuyal@gmail.com) +- [Terrance](mailto:git@terrance.allofti.me) +- [Thom Lu](mailto:thomas.c.lu@gmail.com) +- [Thomas Grainger](mailto:tagrain@gmail.com) +- [Tim Gates](mailto:tim.gates@iress.com) +- [Tim Swast](mailto:swast@google.com) +- [Timo](mailto:timo_tk@hotmail.com) +- Toby Fleming +- [Tom Christie](mailto:tom@tomchristie.com) +- [Tony Narlock](mailto:tony@git-pull.com) +- [Tsuyoshi Hombashi](mailto:tsuyoshi.hombashi@gmail.com) +- [Tushar Chandra](mailto:tusharchandra2018@u.northwestern.edu) +- [Tzu-ping Chung](mailto:uranusjr@gmail.com) +- [Utsav Shah](mailto:ukshah2@illinois.edu) +- utsav-dbx +- vezeli +- [Ville Skyttä](mailto:ville.skytta@iki.fi) +- [Vishwas B Sharma](mailto:sharma.vishwas88@gmail.com) +- [Vlad Emelianov](mailto:volshebnyi@gmail.com) +- [williamfzc](mailto:178894043@qq.com) +- [wouter bolsterlee](mailto:wouter@bolsterl.ee) +- Yazdan +- [Yngve Høiseth](mailto:yngve@hoiseth.net) +- [Yurii Karabas](mailto:1998uriyyo@gmail.com) +- [Zac Hatfield-Dodds](mailto:zac@zhd.dev) diff --git a/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/LICENSE b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/LICENSE new file mode 100644 index 00000000..7a9b891f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black-23.1.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Łukasz Langa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.11/site-packages/black/__init__.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/__init__.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..0fcc9208c1fc6ea3b48d424c778fc8f0b0ffe9d6 GIT binary patch literal 8248 zcmeHMU2Ggz6~61W<89;GCT$v%M9K&pY9lgs{8zV;+x5>n<6x&|V?(G&CbK(hd+Gj| z-PzO*&{|4cYEVQJMU~*mP>`Tc0jZVR2eeH>8=f%bFE1b~QH0$V)up9!ALw$lf#e(vwwIrsR<(CBcL&nLL}#X|zQrEM+}Bth?|sRAS@dPOb#?iG8a zZq25stoDAug_sJVNdVixCN}PpEKp5Wm>N|sK2DVFkVU(udRN|5ugMD2JPm~@H7}|` z3!iHwS822KA_P;^#WH@&v9W%f9I2G`nX=uNz>cO+`uU`vMp6hLz8UA}Vf8XSDD+_ORU%ZG-p!^Pac>bTwpYn<7ff2;&;K6Af z2>tQxS8iVTR^eCszkmH3$M?Q`?%nz~fB#PXFK)eb7{nFJUO=@9l9vJFv3bQ8w!pDp znpctP&jYRG#{yg<9WUwK0uKZJ8LuMMe+#rx+$(~OT8Z?A#CHip!Jh_vhuAL8*ShKt zkky|^U-g51-vKkPDVyS6iPwob-l*g!C-LoKS@ADSb2^z$nOVomIHoDgcq;A)a|(zs zM<&KhJC=z}$Foi>Gch)pNT*^GRwNN~WtAmnbk0HtRw8}|6px%X!{lKwVP&(itQemk zfmTdYHU^CnA4yoz$AB5MX0y3O%p6J1rZWyoWrg^$v<*UGj?bGzF)NWsM@1qYiR#(3 z-XqMIZ8;VUDw55*V*)5;!}c8+7&JTd4!xTu#6zltujCI!i2NET?~&Z@IBe-E@i(&L zbA_jQoaR-~`N4Q2;f*Rz{PhM+dcfF97m736WM)|MszOXP6~YkA4PfIN16?ULkwj68OE zZ3%eeT+I>y#<{)=KqigW4P)_^adxBr@v0M7pkw!-5IXjc6NMFQAN1p{e#E2y>W1G~ zyllMs=EH_>+4$W?P8#ogCxFIpfU&XoTHyxTFZ8v7oygs1obCHFh@r{FTTUIce`A{_ zR!=~eR%hVn#A`JI6n*bqL8~dfVc%2GWdT#ucL@}Q^Ev3;cclNkvwITke+V%6DlDH^ z`wBQ{*tdZCYhQ%=ruq4C(EK*wt2aM_!Nb5lYZL;xfU)@c>W>gP*Y{139{gbSd6N4d zEvy{OKMLJg0D(aJKA=VUroH9b;{t+xRIvwCt~D-gPo(pK`)#|YE7sl~>9(e(Iy!CB zOwP~FN5#j;bUcep_f+uh@gHgipSZBA|0)!MKzm`RGeGBnE&#=X-z+S?GhyMIYxM1@ zs|}oo-Ga9GO<4llQ$R(1<8Xb`k%sM$2NuM`yAOT7vsoVFXg>tcH5iUOgZt~{5rFvD z;8|C2fNq!DGjMwbZqLB~^$c+Q!|@JJ2FE=d+h9zE1@8t}IDVyTTQP3s>mkQkdt7x9 zAiA3<$6I)|V`2F}KPse=U#EsSHn~JW4aZLH6#U$Y%23{O2}I&G4R7JpYs?V7}{+R3l;Sni}{&S{ZBz$rg%hn8eHH^9=dn^BP!Lo`GK<+;;iA zC0P6vDwW2y)p)N;KMi7CS@$<2UU?lW`TwibFJIrcBwoJ0?*oq6CUqayLPvcS@-TN3 zuC9A4;F|PR`V#`YQa)!?;-$LC9S3{|eC5HpfMXtM znLaq2NBTMG^FHCGUIe^SyT922zX^DyJQbdg@&d(mFLq~@&b0$Z@bvv6jXRMTsnW~|$9_=3+83YaZu9+djbj^&RFhG!1V+W99 z9q1o59~mA#7Md_8`Ugfs;P&H^aFp%xq{7_HU0DuI$?|QHQo~G{nwu+0rB%q~$^KPL z1eRp=%#y5NFz8Llxj9FnXXlfS6#?pGTsp(GRN9H@)2W;;7vMnL7LuE>vNJ-r=Tl(m zQYYg|C?+UjOps+_2@4IVHk)vS%rXewiOoS><~{XHT4oXT*bMED85?Ywb1mFGrqfcQ)~yk$Ua%gcoh1P2Ji5r%O)W^DRkJrN!XIg3-e#H_}#!)p5s!EW0*E8ekp7$ zqWFFJ0AfhC=Xi%{kTf~IRE~G}4~~#AGuv}q#B>492Q1hwR@t7vq(Q*YH`{ak#FXQ5 zP?3e)F)IwHVT{c79A`1rNR#`|dQ2xljxjg$9FH+&d$s@MR@hKM8QXK*#xzfg+`c;g z6xk1u9^V_7&Ux%H$FJI>9Os0?2EJD@E#8;N5BAktvcP!lgXDfj?LSL-2s{VA01x+v&tH7MxbI&37CdaA>`s?7xM@_he~=pZkQIrl_WuUR ChFGQm literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/__init__.py b/.venv/lib/python3.11/site-packages/black/__init__.py new file mode 100644 index 00000000..4ebf2882 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/__init__.py @@ -0,0 +1,1451 @@ +import io +import json +import platform +import re +import sys +import tokenize +import traceback +from contextlib import contextmanager +from dataclasses import replace +from datetime import datetime +from enum import Enum +from json.decoder import JSONDecodeError +from pathlib import Path +from typing import ( + Any, + Dict, + Generator, + Iterator, + List, + MutableMapping, + Optional, + Pattern, + Sequence, + Set, + Sized, + Tuple, + Union, +) + +import click +from click.core import ParameterSource +from mypy_extensions import mypyc_attr +from pathspec import PathSpec +from pathspec.patterns.gitwildmatch import GitWildMatchPatternError + +from _black_version import version as __version__ +from black.cache import Cache, get_cache_info, read_cache, write_cache +from black.comments import normalize_fmt_off +from black.const import ( + DEFAULT_EXCLUDES, + DEFAULT_INCLUDES, + DEFAULT_LINE_LENGTH, + STDIN_PLACEHOLDER, +) +from black.files import ( + find_project_root, + find_pyproject_toml, + find_user_pyproject_toml, + gen_python_files, + get_gitignore, + normalize_path_maybe_ignore, + parse_pyproject_toml, + wrap_stream_for_windows, +) +from black.handle_ipynb_magics import ( + PYTHON_CELL_MAGICS, + TRANSFORMED_MAGICS, + jupyter_dependencies_are_installed, + mask_cell, + put_trailing_semicolon_back, + remove_trailing_semicolon, + unmask_cell, +) +from black.linegen import LN, LineGenerator, transform_line +from black.lines import EmptyLineTracker, LinesBlock +from black.mode import ( + FUTURE_FLAG_TO_FEATURE, + VERSION_TO_FEATURES, + Feature, + Mode, + TargetVersion, + supports_feature, +) +from black.nodes import ( + STARS, + is_number_token, + is_simple_decorator_expression, + is_string_token, + syms, +) +from black.output import color_diff, diff, dump_to_file, err, ipynb_diff, out +from black.parsing import InvalidInput # noqa F401 +from black.parsing import lib2to3_parse, parse_ast, stringify_ast +from black.report import Changed, NothingChanged, Report +from black.trans import iter_fexpr_spans +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + +COMPILED = Path(__file__).suffix in (".pyd", ".so") + +# types +FileContent = str +Encoding = str +NewLine = str + + +class WriteBack(Enum): + NO = 0 + YES = 1 + DIFF = 2 + CHECK = 3 + COLOR_DIFF = 4 + + @classmethod + def from_configuration( + cls, *, check: bool, diff: bool, color: bool = False + ) -> "WriteBack": + if check and not diff: + return cls.CHECK + + if diff and color: + return cls.COLOR_DIFF + + return cls.DIFF if diff else cls.YES + + +# Legacy name, left for integrations. +FileMode = Mode + + +def read_pyproject_toml( + ctx: click.Context, param: click.Parameter, value: Optional[str] +) -> Optional[str]: + """Inject Black configuration from "pyproject.toml" into defaults in `ctx`. + + Returns the path to a successfully found and read configuration file, None + otherwise. + """ + if not value: + value = find_pyproject_toml(ctx.params.get("src", ())) + if value is None: + return None + + try: + config = parse_pyproject_toml(value) + except (OSError, ValueError) as e: + raise click.FileError( + filename=value, hint=f"Error reading configuration file: {e}" + ) from None + + if not config: + return None + else: + # Sanitize the values to be Click friendly. For more information please see: + # https://github.com/psf/black/issues/1458 + # https://github.com/pallets/click/issues/1567 + config = { + k: str(v) if not isinstance(v, (list, dict)) else v + for k, v in config.items() + } + + target_version = config.get("target_version") + if target_version is not None and not isinstance(target_version, list): + raise click.BadOptionUsage( + "target-version", "Config key target-version must be a list" + ) + + default_map: Dict[str, Any] = {} + if ctx.default_map: + default_map.update(ctx.default_map) + default_map.update(config) + + ctx.default_map = default_map + return value + + +def target_version_option_callback( + c: click.Context, p: Union[click.Option, click.Parameter], v: Tuple[str, ...] +) -> List[TargetVersion]: + """Compute the target versions from a --target-version flag. + + This is its own function because mypy couldn't infer the type correctly + when it was a lambda, causing mypyc trouble. + """ + return [TargetVersion[val.upper()] for val in v] + + +def re_compile_maybe_verbose(regex: str) -> Pattern[str]: + """Compile a regular expression string in `regex`. + + If it contains newlines, use verbose mode. + """ + if "\n" in regex: + regex = "(?x)" + regex + compiled: Pattern[str] = re.compile(regex) + return compiled + + +def validate_regex( + ctx: click.Context, + param: click.Parameter, + value: Optional[str], +) -> Optional[Pattern[str]]: + try: + return re_compile_maybe_verbose(value) if value is not None else None + except re.error as e: + raise click.BadParameter(f"Not a valid regular expression: {e}") from None + + +@click.command( + context_settings={"help_option_names": ["-h", "--help"]}, + # While Click does set this field automatically using the docstring, mypyc + # (annoyingly) strips 'em so we need to set it here too. + help="The uncompromising code formatter.", +) +@click.option("-c", "--code", type=str, help="Format the code passed in as a string.") +@click.option( + "-l", + "--line-length", + type=int, + default=DEFAULT_LINE_LENGTH, + help="How many characters per line to allow.", + show_default=True, +) +@click.option( + "-t", + "--target-version", + type=click.Choice([v.name.lower() for v in TargetVersion]), + callback=target_version_option_callback, + multiple=True, + help=( + "Python versions that should be supported by Black's output. By default, Black" + " will try to infer this from the project metadata in pyproject.toml. If this" + " does not yield conclusive results, Black will use per-file auto-detection." + ), +) +@click.option( + "--pyi", + is_flag=True, + help=( + "Format all input files like typing stubs regardless of file extension (useful" + " when piping source on standard input)." + ), +) +@click.option( + "--ipynb", + is_flag=True, + help=( + "Format all input files like Jupyter Notebooks regardless of file extension " + "(useful when piping source on standard input)." + ), +) +@click.option( + "--python-cell-magics", + multiple=True, + help=( + "When processing Jupyter Notebooks, add the given magic to the list" + f" of known python-magics ({', '.join(sorted(PYTHON_CELL_MAGICS))})." + " Useful for formatting cells with custom python magics." + ), + default=[], +) +@click.option( + "-x", + "--skip-source-first-line", + is_flag=True, + help="Skip the first line of the source code.", +) +@click.option( + "-S", + "--skip-string-normalization", + is_flag=True, + help="Don't normalize string quotes or prefixes.", +) +@click.option( + "-C", + "--skip-magic-trailing-comma", + is_flag=True, + help="Don't use trailing commas as a reason to split lines.", +) +@click.option( + "--experimental-string-processing", + is_flag=True, + hidden=True, + help="(DEPRECATED and now included in --preview) Normalize string literals.", +) +@click.option( + "--preview", + is_flag=True, + help=( + "Enable potentially disruptive style changes that may be added to Black's main" + " functionality in the next major release." + ), +) +@click.option( + "--check", + is_flag=True, + help=( + "Don't write the files back, just return the status. Return code 0 means" + " nothing would change. Return code 1 means some files would be reformatted." + " Return code 123 means there was an internal error." + ), +) +@click.option( + "--diff", + is_flag=True, + help="Don't write the files back, just output a diff for each file on stdout.", +) +@click.option( + "--color/--no-color", + is_flag=True, + help="Show colored diff. Only applies when `--diff` is given.", +) +@click.option( + "--fast/--safe", + is_flag=True, + help="If --fast given, skip temporary sanity checks. [default: --safe]", +) +@click.option( + "--required-version", + type=str, + help=( + "Require a specific version of Black to be running (useful for unifying results" + " across many environments e.g. with a pyproject.toml file). It can be" + " either a major version number or an exact version." + ), +) +@click.option( + "--include", + type=str, + default=DEFAULT_INCLUDES, + callback=validate_regex, + help=( + "A regular expression that matches files and directories that should be" + " included on recursive searches. An empty value means all files are included" + " regardless of the name. Use forward slashes for directories on all platforms" + " (Windows, too). Exclusions are calculated first, inclusions later." + ), + show_default=True, +) +@click.option( + "--exclude", + type=str, + callback=validate_regex, + help=( + "A regular expression that matches files and directories that should be" + " excluded on recursive searches. An empty value means no paths are excluded." + " Use forward slashes for directories on all platforms (Windows, too)." + " Exclusions are calculated first, inclusions later. [default:" + f" {DEFAULT_EXCLUDES}]" + ), + show_default=False, +) +@click.option( + "--extend-exclude", + type=str, + callback=validate_regex, + help=( + "Like --exclude, but adds additional files and directories on top of the" + " excluded ones. (Useful if you simply want to add to the default)" + ), +) +@click.option( + "--force-exclude", + type=str, + callback=validate_regex, + help=( + "Like --exclude, but files and directories matching this regex will be " + "excluded even when they are passed explicitly as arguments." + ), +) +@click.option( + "--stdin-filename", + type=str, + help=( + "The name of the file when passing it through stdin. Useful to make " + "sure Black will respect --force-exclude option on some " + "editors that rely on using stdin." + ), +) +@click.option( + "-W", + "--workers", + type=click.IntRange(min=1), + default=None, + help="Number of parallel workers [default: number of CPUs in the system]", +) +@click.option( + "-q", + "--quiet", + is_flag=True, + help=( + "Don't emit non-error messages to stderr. Errors are still emitted; silence" + " those with 2>/dev/null." + ), +) +@click.option( + "-v", + "--verbose", + is_flag=True, + help=( + "Also emit messages to stderr about files that were not changed or were ignored" + " due to exclusion patterns." + ), +) +@click.version_option( + version=__version__, + message=( + f"%(prog)s, %(version)s (compiled: {'yes' if COMPILED else 'no'})\n" + f"Python ({platform.python_implementation()}) {platform.python_version()}" + ), +) +@click.argument( + "src", + nargs=-1, + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True + ), + is_eager=True, + metavar="SRC ...", +) +@click.option( + "--config", + type=click.Path( + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + allow_dash=False, + path_type=str, + ), + is_eager=True, + callback=read_pyproject_toml, + help="Read configuration from FILE path.", +) +@click.pass_context +def main( # noqa: C901 + ctx: click.Context, + code: Optional[str], + line_length: int, + target_version: List[TargetVersion], + check: bool, + diff: bool, + color: bool, + fast: bool, + pyi: bool, + ipynb: bool, + python_cell_magics: Sequence[str], + skip_source_first_line: bool, + skip_string_normalization: bool, + skip_magic_trailing_comma: bool, + experimental_string_processing: bool, + preview: bool, + quiet: bool, + verbose: bool, + required_version: Optional[str], + include: Pattern[str], + exclude: Optional[Pattern[str]], + extend_exclude: Optional[Pattern[str]], + force_exclude: Optional[Pattern[str]], + stdin_filename: Optional[str], + workers: Optional[int], + src: Tuple[str, ...], + config: Optional[str], +) -> None: + """The uncompromising code formatter.""" + ctx.ensure_object(dict) + + if src and code is not None: + out( + main.get_usage(ctx) + + "\n\n'SRC' and 'code' cannot be passed simultaneously." + ) + ctx.exit(1) + if not src and code is None: + out(main.get_usage(ctx) + "\n\nOne of 'SRC' or 'code' is required.") + ctx.exit(1) + + root, method = ( + find_project_root(src, stdin_filename) if code is None else (None, None) + ) + ctx.obj["root"] = root + + if verbose: + if root: + out( + f"Identified `{root}` as project root containing a {method}.", + fg="blue", + ) + + normalized = [ + ( + (source, source) + if source == "-" + else (normalize_path_maybe_ignore(Path(source), root), source) + ) + for source in src + ] + srcs_string = ", ".join( + [ + ( + f'"{_norm}"' + if _norm + else f'\033[31m"{source} (skipping - invalid)"\033[34m' + ) + for _norm, source in normalized + ] + ) + out(f"Sources to be formatted: {srcs_string}", fg="blue") + + if config: + config_source = ctx.get_parameter_source("config") + user_level_config = str(find_user_pyproject_toml()) + if config == user_level_config: + out( + ( + "Using configuration from user-level config at " + f"'{user_level_config}'." + ), + fg="blue", + ) + elif config_source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + out("Using configuration from project root.", fg="blue") + else: + out(f"Using configuration in '{config}'.", fg="blue") + if ctx.default_map: + for param, value in ctx.default_map.items(): + out(f"{param}: {value}") + + error_msg = "Oh no! 💥 💔 💥" + if ( + required_version + and required_version != __version__ + and required_version != __version__.split(".")[0] + ): + err( + f"{error_msg} The required version `{required_version}` does not match" + f" the running version `{__version__}`!" + ) + ctx.exit(1) + if ipynb and pyi: + err("Cannot pass both `pyi` and `ipynb` flags!") + ctx.exit(1) + + write_back = WriteBack.from_configuration(check=check, diff=diff, color=color) + if target_version: + versions = set(target_version) + else: + # We'll autodetect later. + versions = set() + mode = Mode( + target_versions=versions, + line_length=line_length, + is_pyi=pyi, + is_ipynb=ipynb, + skip_source_first_line=skip_source_first_line, + string_normalization=not skip_string_normalization, + magic_trailing_comma=not skip_magic_trailing_comma, + experimental_string_processing=experimental_string_processing, + preview=preview, + python_cell_magics=set(python_cell_magics), + ) + + if code is not None: + # Run in quiet mode by default with -c; the extra output isn't useful. + # You can still pass -v to get verbose output. + quiet = True + + report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) + + if code is not None: + reformat_code( + content=code, fast=fast, write_back=write_back, mode=mode, report=report + ) + else: + try: + sources = get_sources( + ctx=ctx, + src=src, + quiet=quiet, + verbose=verbose, + include=include, + exclude=exclude, + extend_exclude=extend_exclude, + force_exclude=force_exclude, + report=report, + stdin_filename=stdin_filename, + ) + except GitWildMatchPatternError: + ctx.exit(1) + + path_empty( + sources, + "No Python files are present to be formatted. Nothing to do 😴", + quiet, + verbose, + ctx, + ) + + if len(sources) == 1: + reformat_one( + src=sources.pop(), + fast=fast, + write_back=write_back, + mode=mode, + report=report, + ) + else: + from black.concurrency import reformat_many + + reformat_many( + sources=sources, + fast=fast, + write_back=write_back, + mode=mode, + report=report, + workers=workers, + ) + + if verbose or not quiet: + if code is None and (verbose or report.change_count or report.failure_count): + out() + out(error_msg if report.return_code else "All done! ✨ 🍰 ✨") + if code is None: + click.echo(str(report), err=True) + ctx.exit(report.return_code) + + +def get_sources( + *, + ctx: click.Context, + src: Tuple[str, ...], + quiet: bool, + verbose: bool, + include: Pattern[str], + exclude: Optional[Pattern[str]], + extend_exclude: Optional[Pattern[str]], + force_exclude: Optional[Pattern[str]], + report: "Report", + stdin_filename: Optional[str], +) -> Set[Path]: + """Compute the set of files to be formatted.""" + sources: Set[Path] = set() + root = ctx.obj["root"] + + using_default_exclude = exclude is None + exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES) if exclude is None else exclude + gitignore: Optional[Dict[Path, PathSpec]] = None + root_gitignore = get_gitignore(root) + + for s in src: + if s == "-" and stdin_filename: + p = Path(stdin_filename) + is_stdin = True + else: + p = Path(s) + is_stdin = False + + if is_stdin or p.is_file(): + normalized_path = normalize_path_maybe_ignore(p, ctx.obj["root"], report) + if normalized_path is None: + continue + + normalized_path = "/" + normalized_path + # Hard-exclude any files that matches the `--force-exclude` regex. + if force_exclude: + force_exclude_match = force_exclude.search(normalized_path) + else: + force_exclude_match = None + if force_exclude_match and force_exclude_match.group(0): + report.path_ignored(p, "matches the --force-exclude regular expression") + continue + + if is_stdin: + p = Path(f"{STDIN_PLACEHOLDER}{str(p)}") + + if p.suffix == ".ipynb" and not jupyter_dependencies_are_installed( + verbose=verbose, quiet=quiet + ): + continue + + sources.add(p) + elif p.is_dir(): + p = root / normalize_path_maybe_ignore(p, ctx.obj["root"], report) + if using_default_exclude: + gitignore = { + root: root_gitignore, + p: get_gitignore(p), + } + sources.update( + gen_python_files( + p.iterdir(), + ctx.obj["root"], + include, + exclude, + extend_exclude, + force_exclude, + report, + gitignore, + verbose=verbose, + quiet=quiet, + ) + ) + elif s == "-": + sources.add(p) + else: + err(f"invalid path: {s}") + return sources + + +def path_empty( + src: Sized, msg: str, quiet: bool, verbose: bool, ctx: click.Context +) -> None: + """ + Exit if there is no `src` provided for formatting + """ + if not src: + if verbose or not quiet: + out(msg) + ctx.exit(0) + + +def reformat_code( + content: str, fast: bool, write_back: WriteBack, mode: Mode, report: Report +) -> None: + """ + Reformat and print out `content` without spawning child processes. + Similar to `reformat_one`, but for string content. + + `fast`, `write_back`, and `mode` options are passed to + :func:`format_file_in_place` or :func:`format_stdin_to_stdout`. + """ + path = Path("") + try: + changed = Changed.NO + if format_stdin_to_stdout( + content=content, fast=fast, write_back=write_back, mode=mode + ): + changed = Changed.YES + report.done(path, changed) + except Exception as exc: + if report.verbose: + traceback.print_exc() + report.failed(path, str(exc)) + + +# diff-shades depends on being to monkeypatch this function to operate. I know it's +# not ideal, but this shouldn't cause any issues ... hopefully. ~ichard26 +@mypyc_attr(patchable=True) +def reformat_one( + src: Path, fast: bool, write_back: WriteBack, mode: Mode, report: "Report" +) -> None: + """Reformat a single file under `src` without spawning child processes. + + `fast`, `write_back`, and `mode` options are passed to + :func:`format_file_in_place` or :func:`format_stdin_to_stdout`. + """ + try: + changed = Changed.NO + + if str(src) == "-": + is_stdin = True + elif str(src).startswith(STDIN_PLACEHOLDER): + is_stdin = True + # Use the original name again in case we want to print something + # to the user + src = Path(str(src)[len(STDIN_PLACEHOLDER) :]) + else: + is_stdin = False + + if is_stdin: + if src.suffix == ".pyi": + mode = replace(mode, is_pyi=True) + elif src.suffix == ".ipynb": + mode = replace(mode, is_ipynb=True) + if format_stdin_to_stdout(fast=fast, write_back=write_back, mode=mode): + changed = Changed.YES + else: + cache: Cache = {} + if write_back not in (WriteBack.DIFF, WriteBack.COLOR_DIFF): + cache = read_cache(mode) + res_src = src.resolve() + res_src_s = str(res_src) + if res_src_s in cache and cache[res_src_s] == get_cache_info(res_src): + changed = Changed.CACHED + if changed is not Changed.CACHED and format_file_in_place( + src, fast=fast, write_back=write_back, mode=mode + ): + changed = Changed.YES + if (write_back is WriteBack.YES and changed is not Changed.CACHED) or ( + write_back is WriteBack.CHECK and changed is Changed.NO + ): + write_cache(cache, [src], mode) + report.done(src, changed) + except Exception as exc: + if report.verbose: + traceback.print_exc() + report.failed(src, str(exc)) + + +def format_file_in_place( + src: Path, + fast: bool, + mode: Mode, + write_back: WriteBack = WriteBack.NO, + lock: Any = None, # multiprocessing.Manager().Lock() is some crazy proxy +) -> bool: + """Format file under `src` path. Return True if changed. + + If `write_back` is DIFF, write a diff to stdout. If it is YES, write reformatted + code to the file. + `mode` and `fast` options are passed to :func:`format_file_contents`. + """ + if src.suffix == ".pyi": + mode = replace(mode, is_pyi=True) + elif src.suffix == ".ipynb": + mode = replace(mode, is_ipynb=True) + + then = datetime.utcfromtimestamp(src.stat().st_mtime) + header = b"" + with open(src, "rb") as buf: + if mode.skip_source_first_line: + header = buf.readline() + src_contents, encoding, newline = decode_bytes(buf.read()) + try: + dst_contents = format_file_contents(src_contents, fast=fast, mode=mode) + except NothingChanged: + return False + except JSONDecodeError: + raise ValueError( + f"File '{src}' cannot be parsed as valid Jupyter notebook." + ) from None + src_contents = header.decode(encoding) + src_contents + dst_contents = header.decode(encoding) + dst_contents + + if write_back == WriteBack.YES: + with open(src, "w", encoding=encoding, newline=newline) as f: + f.write(dst_contents) + elif write_back in (WriteBack.DIFF, WriteBack.COLOR_DIFF): + now = datetime.utcnow() + src_name = f"{src}\t{then} +0000" + dst_name = f"{src}\t{now} +0000" + if mode.is_ipynb: + diff_contents = ipynb_diff(src_contents, dst_contents, src_name, dst_name) + else: + diff_contents = diff(src_contents, dst_contents, src_name, dst_name) + + if write_back == WriteBack.COLOR_DIFF: + diff_contents = color_diff(diff_contents) + + with lock or nullcontext(): + f = io.TextIOWrapper( + sys.stdout.buffer, + encoding=encoding, + newline=newline, + write_through=True, + ) + f = wrap_stream_for_windows(f) + f.write(diff_contents) + f.detach() + + return True + + +def format_stdin_to_stdout( + fast: bool, + *, + content: Optional[str] = None, + write_back: WriteBack = WriteBack.NO, + mode: Mode, +) -> bool: + """Format file on stdin. Return True if changed. + + If content is None, it's read from sys.stdin. + + If `write_back` is YES, write reformatted code back to stdout. If it is DIFF, + write a diff to stdout. The `mode` argument is passed to + :func:`format_file_contents`. + """ + then = datetime.utcnow() + + if content is None: + src, encoding, newline = decode_bytes(sys.stdin.buffer.read()) + else: + src, encoding, newline = content, "utf-8", "" + + dst = src + try: + dst = format_file_contents(src, fast=fast, mode=mode) + return True + + except NothingChanged: + return False + + finally: + f = io.TextIOWrapper( + sys.stdout.buffer, encoding=encoding, newline=newline, write_through=True + ) + if write_back == WriteBack.YES: + # Make sure there's a newline after the content + if dst and dst[-1] != "\n": + dst += "\n" + f.write(dst) + elif write_back in (WriteBack.DIFF, WriteBack.COLOR_DIFF): + now = datetime.utcnow() + src_name = f"STDIN\t{then} +0000" + dst_name = f"STDOUT\t{now} +0000" + d = diff(src, dst, src_name, dst_name) + if write_back == WriteBack.COLOR_DIFF: + d = color_diff(d) + f = wrap_stream_for_windows(f) + f.write(d) + f.detach() + + +def check_stability_and_equivalence( + src_contents: str, dst_contents: str, *, mode: Mode +) -> None: + """Perform stability and equivalence checks. + + Raise AssertionError if source and destination contents are not + equivalent, or if a second pass of the formatter would format the + content differently. + """ + assert_equivalent(src_contents, dst_contents) + assert_stable(src_contents, dst_contents, mode=mode) + + +def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileContent: + """Reformat contents of a file and return new contents. + + If `fast` is False, additionally confirm that the reformatted code is + valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it. + `mode` is passed to :func:`format_str`. + """ + if mode.is_ipynb: + dst_contents = format_ipynb_string(src_contents, fast=fast, mode=mode) + else: + dst_contents = format_str(src_contents, mode=mode) + if src_contents == dst_contents: + raise NothingChanged + + if not fast and not mode.is_ipynb: + # Jupyter notebooks will already have been checked above. + check_stability_and_equivalence(src_contents, dst_contents, mode=mode) + return dst_contents + + +def validate_cell(src: str, mode: Mode) -> None: + """Check that cell does not already contain TransformerManager transformations, + or non-Python cell magics, which might cause tokenizer_rt to break because of + indentations. + + If a cell contains ``!ls``, then it'll be transformed to + ``get_ipython().system('ls')``. However, if the cell originally contained + ``get_ipython().system('ls')``, then it would get transformed in the same way: + + >>> TransformerManager().transform_cell("get_ipython().system('ls')") + "get_ipython().system('ls')\n" + >>> TransformerManager().transform_cell("!ls") + "get_ipython().system('ls')\n" + + Due to the impossibility of safely roundtripping in such situations, cells + containing transformed magics will be ignored. + """ + if any(transformed_magic in src for transformed_magic in TRANSFORMED_MAGICS): + raise NothingChanged + if ( + src[:2] == "%%" + and src.split()[0][2:] not in PYTHON_CELL_MAGICS | mode.python_cell_magics + ): + raise NothingChanged + + +def format_cell(src: str, *, fast: bool, mode: Mode) -> str: + """Format code in given cell of Jupyter notebook. + + General idea is: + + - if cell has trailing semicolon, remove it; + - if cell has IPython magics, mask them; + - format cell; + - reinstate IPython magics; + - reinstate trailing semicolon (if originally present); + - strip trailing newlines. + + Cells with syntax errors will not be processed, as they + could potentially be automagics or multi-line magics, which + are currently not supported. + """ + validate_cell(src, mode) + src_without_trailing_semicolon, has_trailing_semicolon = remove_trailing_semicolon( + src + ) + try: + masked_src, replacements = mask_cell(src_without_trailing_semicolon) + except SyntaxError: + raise NothingChanged from None + masked_dst = format_str(masked_src, mode=mode) + if not fast: + check_stability_and_equivalence(masked_src, masked_dst, mode=mode) + dst_without_trailing_semicolon = unmask_cell(masked_dst, replacements) + dst = put_trailing_semicolon_back( + dst_without_trailing_semicolon, has_trailing_semicolon + ) + dst = dst.rstrip("\n") + if dst == src: + raise NothingChanged from None + return dst + + +def validate_metadata(nb: MutableMapping[str, Any]) -> None: + """If notebook is marked as non-Python, don't format it. + + All notebook metadata fields are optional, see + https://nbformat.readthedocs.io/en/latest/format_description.html. So + if a notebook has empty metadata, we will try to parse it anyway. + """ + language = nb.get("metadata", {}).get("language_info", {}).get("name", None) + if language is not None and language != "python": + raise NothingChanged from None + + +def format_ipynb_string(src_contents: str, *, fast: bool, mode: Mode) -> FileContent: + """Format Jupyter notebook. + + Operate cell-by-cell, only on code cells, only for Python notebooks. + If the ``.ipynb`` originally had a trailing newline, it'll be preserved. + """ + if not src_contents: + raise NothingChanged + + trailing_newline = src_contents[-1] == "\n" + modified = False + nb = json.loads(src_contents) + validate_metadata(nb) + for cell in nb["cells"]: + if cell.get("cell_type", None) == "code": + try: + src = "".join(cell["source"]) + dst = format_cell(src, fast=fast, mode=mode) + except NothingChanged: + pass + else: + cell["source"] = dst.splitlines(keepends=True) + modified = True + if modified: + dst_contents = json.dumps(nb, indent=1, ensure_ascii=False) + if trailing_newline: + dst_contents = dst_contents + "\n" + return dst_contents + else: + raise NothingChanged + + +def format_str(src_contents: str, *, mode: Mode) -> str: + """Reformat a string and return new contents. + + `mode` determines formatting options, such as how many characters per line are + allowed. Example: + + >>> import black + >>> print(black.format_str("def f(arg:str='')->None:...", mode=black.Mode())) + def f(arg: str = "") -> None: + ... + + A more complex example: + + >>> print( + ... black.format_str( + ... "def f(arg:str='')->None: hey", + ... mode=black.Mode( + ... target_versions={black.TargetVersion.PY36}, + ... line_length=10, + ... string_normalization=False, + ... is_pyi=False, + ... ), + ... ), + ... ) + def f( + arg: str = '', + ) -> None: + hey + + """ + dst_contents = _format_str_once(src_contents, mode=mode) + # Forced second pass to work around optional trailing commas (becoming + # forced trailing commas on pass 2) interacting differently with optional + # parentheses. Admittedly ugly. + if src_contents != dst_contents: + return _format_str_once(dst_contents, mode=mode) + return dst_contents + + +def _format_str_once(src_contents: str, *, mode: Mode) -> str: + src_node = lib2to3_parse(src_contents.lstrip(), mode.target_versions) + dst_blocks: List[LinesBlock] = [] + if mode.target_versions: + versions = mode.target_versions + else: + future_imports = get_future_imports(src_node) + versions = detect_target_versions(src_node, future_imports=future_imports) + + context_manager_features = { + feature + for feature in {Feature.PARENTHESIZED_CONTEXT_MANAGERS} + if supports_feature(versions, feature) + } + normalize_fmt_off(src_node) + lines = LineGenerator(mode=mode, features=context_manager_features) + elt = EmptyLineTracker(mode=mode) + split_line_features = { + feature + for feature in {Feature.TRAILING_COMMA_IN_CALL, Feature.TRAILING_COMMA_IN_DEF} + if supports_feature(versions, feature) + } + block: Optional[LinesBlock] = None + for current_line in lines.visit(src_node): + block = elt.maybe_empty_lines(current_line) + dst_blocks.append(block) + for line in transform_line( + current_line, mode=mode, features=split_line_features + ): + block.content_lines.append(str(line)) + if dst_blocks: + dst_blocks[-1].after = 0 + dst_contents = [] + for block in dst_blocks: + dst_contents.extend(block.all_lines()) + if not dst_contents: + # Use decode_bytes to retrieve the correct source newline (CRLF or LF), + # and check if normalized_content has more than one line + normalized_content, _, newline = decode_bytes(src_contents.encode("utf-8")) + if "\n" in normalized_content: + return newline + return "" + return "".join(dst_contents) + + +def decode_bytes(src: bytes) -> Tuple[FileContent, Encoding, NewLine]: + """Return a tuple of (decoded_contents, encoding, newline). + + `newline` is either CRLF or LF but `decoded_contents` is decoded with + universal newlines (i.e. only contains LF). + """ + srcbuf = io.BytesIO(src) + encoding, lines = tokenize.detect_encoding(srcbuf.readline) + if not lines: + return "", encoding, "\n" + + newline = "\r\n" if b"\r\n" == lines[0][-2:] else "\n" + srcbuf.seek(0) + with io.TextIOWrapper(srcbuf, encoding) as tiow: + return tiow.read(), encoding, newline + + +def get_features_used( # noqa: C901 + node: Node, *, future_imports: Optional[Set[str]] = None +) -> Set[Feature]: + """Return a set of (relatively) new Python features used in this file. + + Currently looking for: + - f-strings; + - self-documenting expressions in f-strings (f"{x=}"); + - underscores in numeric literals; + - trailing commas after * or ** in function signatures and calls; + - positional only arguments in function signatures and lambdas; + - assignment expression; + - relaxed decorator syntax; + - usage of __future__ flags (annotations); + - print / exec statements; + - parenthesized context managers; + - match statements; + - except* clause; + - variadic generics; + """ + features: Set[Feature] = set() + if future_imports: + features |= { + FUTURE_FLAG_TO_FEATURE[future_import] + for future_import in future_imports + if future_import in FUTURE_FLAG_TO_FEATURE + } + + for n in node.pre_order(): + if is_string_token(n): + value_head = n.value[:2] + if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}: + features.add(Feature.F_STRINGS) + if Feature.DEBUG_F_STRINGS not in features: + for span_beg, span_end in iter_fexpr_spans(n.value): + if n.value[span_beg : span_end - 1].rstrip().endswith("="): + features.add(Feature.DEBUG_F_STRINGS) + break + + elif is_number_token(n): + if "_" in n.value: + features.add(Feature.NUMERIC_UNDERSCORES) + + elif n.type == token.SLASH: + if n.parent and n.parent.type in { + syms.typedargslist, + syms.arglist, + syms.varargslist, + }: + features.add(Feature.POS_ONLY_ARGUMENTS) + + elif n.type == token.COLONEQUAL: + features.add(Feature.ASSIGNMENT_EXPRESSIONS) + + elif n.type == syms.decorator: + if len(n.children) > 1 and not is_simple_decorator_expression( + n.children[1] + ): + features.add(Feature.RELAXED_DECORATORS) + + elif ( + n.type in {syms.typedargslist, syms.arglist} + and n.children + and n.children[-1].type == token.COMMA + ): + if n.type == syms.typedargslist: + feature = Feature.TRAILING_COMMA_IN_DEF + else: + feature = Feature.TRAILING_COMMA_IN_CALL + + for ch in n.children: + if ch.type in STARS: + features.add(feature) + + if ch.type == syms.argument: + for argch in ch.children: + if argch.type in STARS: + features.add(feature) + + elif ( + n.type in {syms.return_stmt, syms.yield_expr} + and len(n.children) >= 2 + and n.children[1].type == syms.testlist_star_expr + and any(child.type == syms.star_expr for child in n.children[1].children) + ): + features.add(Feature.UNPACKING_ON_FLOW) + + elif ( + n.type == syms.annassign + and len(n.children) >= 4 + and n.children[3].type == syms.testlist_star_expr + ): + features.add(Feature.ANN_ASSIGN_EXTENDED_RHS) + + elif ( + n.type == syms.with_stmt + and len(n.children) > 2 + and n.children[1].type == syms.atom + ): + atom_children = n.children[1].children + if ( + len(atom_children) == 3 + and atom_children[0].type == token.LPAR + and atom_children[1].type == syms.testlist_gexp + and atom_children[2].type == token.RPAR + ): + features.add(Feature.PARENTHESIZED_CONTEXT_MANAGERS) + + elif n.type == syms.match_stmt: + features.add(Feature.PATTERN_MATCHING) + + elif ( + n.type == syms.except_clause + and len(n.children) >= 2 + and n.children[1].type == token.STAR + ): + features.add(Feature.EXCEPT_STAR) + + elif n.type in {syms.subscriptlist, syms.trailer} and any( + child.type == syms.star_expr for child in n.children + ): + features.add(Feature.VARIADIC_GENERICS) + + elif ( + n.type == syms.tname_star + and len(n.children) == 3 + and n.children[2].type == syms.star_expr + ): + features.add(Feature.VARIADIC_GENERICS) + + return features + + +def detect_target_versions( + node: Node, *, future_imports: Optional[Set[str]] = None +) -> Set[TargetVersion]: + """Detect the version to target based on the nodes used.""" + features = get_features_used(node, future_imports=future_imports) + return { + version for version in TargetVersion if features <= VERSION_TO_FEATURES[version] + } + + +def get_future_imports(node: Node) -> Set[str]: + """Return a set of __future__ imports in the file.""" + imports: Set[str] = set() + + def get_imports_from_children(children: List[LN]) -> Generator[str, None, None]: + for child in children: + if isinstance(child, Leaf): + if child.type == token.NAME: + yield child.value + + elif child.type == syms.import_as_name: + orig_name = child.children[0] + assert isinstance(orig_name, Leaf), "Invalid syntax parsing imports" + assert orig_name.type == token.NAME, "Invalid syntax parsing imports" + yield orig_name.value + + elif child.type == syms.import_as_names: + yield from get_imports_from_children(child.children) + + else: + raise AssertionError("Invalid syntax parsing imports") + + for child in node.children: + if child.type != syms.simple_stmt: + break + + first_child = child.children[0] + if isinstance(first_child, Leaf): + # Continue looking if we see a docstring; otherwise stop. + if ( + len(child.children) == 2 + and first_child.type == token.STRING + and child.children[1].type == token.NEWLINE + ): + continue + + break + + elif first_child.type == syms.import_from: + module_name = first_child.children[1] + if not isinstance(module_name, Leaf) or module_name.value != "__future__": + break + + imports |= set(get_imports_from_children(first_child.children[3:])) + else: + break + + return imports + + +def assert_equivalent(src: str, dst: str) -> None: + """Raise AssertionError if `src` and `dst` aren't equivalent.""" + try: + src_ast = parse_ast(src) + except Exception as exc: + raise AssertionError( + "cannot use --safe with this file; failed to parse source file AST: " + f"{exc}\n" + "This could be caused by running Black with an older Python version " + "that does not support new syntax used in your source file." + ) from exc + + try: + dst_ast = parse_ast(dst) + except Exception as exc: + log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst) + raise AssertionError( + f"INTERNAL ERROR: Black produced invalid code: {exc}. " + "Please report a bug on https://github.com/psf/black/issues. " + f"This invalid output might be helpful: {log}" + ) from None + + src_ast_str = "\n".join(stringify_ast(src_ast)) + dst_ast_str = "\n".join(stringify_ast(dst_ast)) + if src_ast_str != dst_ast_str: + log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst")) + raise AssertionError( + "INTERNAL ERROR: Black produced code that is not equivalent to the" + " source. Please report a bug on " + f"https://github.com/psf/black/issues. This diff might be helpful: {log}" + ) from None + + +def assert_stable(src: str, dst: str, mode: Mode) -> None: + """Raise AssertionError if `dst` reformats differently the second time.""" + # We shouldn't call format_str() here, because that formats the string + # twice and may hide a bug where we bounce back and forth between two + # versions. + newdst = _format_str_once(dst, mode=mode) + if dst != newdst: + log = dump_to_file( + str(mode), + diff(src, dst, "source", "first pass"), + diff(dst, newdst, "first pass", "second pass"), + ) + raise AssertionError( + "INTERNAL ERROR: Black produced different code on the second pass of the" + " formatter. Please report a bug on https://github.com/psf/black/issues." + f" This diff might be helpful: {log}" + ) from None + + +@contextmanager +def nullcontext() -> Iterator[None]: + """Return an empty context manager. + + To be used like `nullcontext` in Python 3.7. + """ + yield + + +def patch_click() -> None: + """Make Click not crash on Python 3.6 with LANG=C. + + On certain misconfigured environments, Python 3 selects the ASCII encoding as the + default which restricts paths that it can access during the lifetime of the + application. Click refuses to work in this scenario by raising a RuntimeError. + + In case of Black the likelihood that non-ASCII characters are going to be used in + file paths is minimal since it's Python source code. Moreover, this crash was + spurious on Python 3.7 thanks to PEP 538 and PEP 540. + """ + modules: List[Any] = [] + try: + from click import core + except ImportError: + pass + else: + modules.append(core) + try: + # Removed in Click 8.1.0 and newer; we keep this around for users who have + # older versions installed. + from click import _unicodefun # type: ignore + except ImportError: + pass + else: + modules.append(_unicodefun) + + for module in modules: + if hasattr(module, "_verify_python3_env"): + module._verify_python3_env = lambda: None + if hasattr(module, "_verify_python_env"): + module._verify_python_env = lambda: None + + +def patched_main() -> None: + # PyInstaller patches multiprocessing to need freeze_support() even in non-Windows + # environments so just assume we always need to call it if frozen. + if getattr(sys, "frozen", False): + from multiprocessing import freeze_support + + freeze_support() + + patch_click() + main() + + +if __name__ == "__main__": + patched_main() diff --git a/.venv/lib/python3.11/site-packages/black/__main__.py b/.venv/lib/python3.11/site-packages/black/__main__.py new file mode 100644 index 00000000..19b810b5 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/__main__.py @@ -0,0 +1,3 @@ +from black import patched_main + +patched_main() diff --git a/.venv/lib/python3.11/site-packages/black/brackets.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/brackets.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..fc2c0c6d09b17465005e6456315f2096dfb5bf10 GIT binary patch literal 8256 zcmeHMYit}>6~61WlWpzVOWP2WSY!k#*i|!j){nXb)AeKRaj;Y4I0XvsbarQKZ@Uk7 zcQ&y@0ZZv421SY@@en`2fT&O)F|LTk(TG zM~>}wXu}WB(4iyZ*tw?b_dh)Pxo_VYdijli-}bBJUvFzb7bw389-jY`dBZ2F`xX(a zg$I|TFY?D%fAPwh$1XnlnWmqedcA%n{hc3Q`TcA4KUsZl2*edj6QD{3$u9xpvAM-x z+XBaaDQ-ooKMk}}I|kqi>9|Sv7Wg>eA95>F{UxA5@qQ5wDkaiYiSHDe2mdqSUVZH2 ztuVuiXYrqqc&#|S9V$@naR_)s{W?*{8^y&;FS1i#)v&)*(mQfX0y43r4J`((-|8TWQF+Av?(R>U3wNK}-7ehiS3B`)K>KRVPj6C$_C(&USvvq8 zv=h$q$v=ZUc76S< zXe}Gs;;MFHqyAXc> z_I;B3A1GYfpMLb;r_B}w0@=bfgvBw1j`&d0<%$W%Ow^k@Q31vo2;r-ShA_8(B7eO%<1-!2qB4)iVb8@nA#kvA}}3ch`YC+xN=j9PJ0- z$-}VZ+1gt#j{?L$56_wh2k1t*F#|Vd;KmI6U(Nu>KOFDyByilrvCT3G>WFe&OV_qy z+{)KOjgU^fX|1#;rXHD`vk5`F* zn&fX1U+gEf_rE55Z}$4VpC){Y=ux6i68$>Sq5l4lDa~->Y*9MYZnaYhwe9b0>k73i z&EuA-Xol_J`#L&WL@Z&Xrj?^sCL2$uly)DIT(Go||P zgLq7FL$dCN8*QK>ew*O)#ckw==&#Z~^ot#W?^~t%aBHUDbt%5H?Ea2&fALh<)W&U7 zEnY8dB2Ep@IbVhT0}h*~ zY##=pqrM7xm?px#>mCC9R+#_ObYOE{r4MjL`Y3%p2zaGDk0tR^UF04Gyi$JV*cSRH z0awbK5#QKC|3?xp&o4a-IF4(@Gp_Tb&-;g6{TA>_elKr0h zCM?@B)vor=&dN$MlcgJ(j4=;cAUiWJrZPs-(#>2lIS(c-&dEl(Ra@=kEIvouV%^h^ z+}+h0&BYUDtGto6@_keeU~s&5G@?fi4PXjQ4u|VzHm%PXDHHQm1D`$AJ38DC8t{SB zBbx4*X#?W`L3)iF7#g{+uXjYhZ*cH%WI~_l?Hh@dC&}J1J4ZP%SAxt*>6K;HJn6qJ z5^b12^Cs;|5^NQce6oMr!l~d(n?2DU1 zax+GDMyTd|3M?IJXB-K|2PKRSvW%55(12>Q30ugFgHUa24(c)os%FwMov2zfv{zp5r5?3vfX4I+<(?%dIaPcdo#~*8B@0R_Me&+CR9+y z_8h-4&66Vg_l`eB_I;$s_XnnY-#~lJ^?U76j&s7{1K+Qh7Vk} z=|gU1sB?at>6bkAOJvV9?pZLl<7OZC*z^A}nDRY|^}YT73fYH9f#X%ChuDxPcZca2 zvgh^N>i=`xjMx8?$DZR~rrw`8uOGMf3{>jj;r+q!82|sytIs^s=fJkXWnYY^u~8_j z=Wv@mf|!3Y2Dt6yI#m^fE}?<1faChVQ P(SDE|yvd40z4reBk=12I literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/brackets.py b/.venv/lib/python3.11/site-packages/black/brackets.py new file mode 100644 index 00000000..343f0608 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/brackets.py @@ -0,0 +1,381 @@ +"""Builds on top of nodes.py to track brackets.""" + +import sys +from dataclasses import dataclass, field +from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union + +if sys.version_info < (3, 8): + from typing_extensions import Final +else: + from typing import Final + +from black.nodes import ( + BRACKET, + CLOSING_BRACKETS, + COMPARATORS, + LOGIC_OPERATORS, + MATH_OPERATORS, + OPENING_BRACKETS, + UNPACKING_PARENTS, + VARARGS_PARENTS, + is_vararg, + syms, +) +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + +# types +LN = Union[Leaf, Node] +Depth = int +LeafID = int +NodeType = int +Priority = int + + +COMPREHENSION_PRIORITY: Final = 20 +COMMA_PRIORITY: Final = 18 +TERNARY_PRIORITY: Final = 16 +LOGIC_PRIORITY: Final = 14 +STRING_PRIORITY: Final = 12 +COMPARATOR_PRIORITY: Final = 10 +MATH_PRIORITIES: Final = { + token.VBAR: 9, + token.CIRCUMFLEX: 8, + token.AMPER: 7, + token.LEFTSHIFT: 6, + token.RIGHTSHIFT: 6, + token.PLUS: 5, + token.MINUS: 5, + token.STAR: 4, + token.SLASH: 4, + token.DOUBLESLASH: 4, + token.PERCENT: 4, + token.AT: 4, + token.TILDE: 3, + token.DOUBLESTAR: 2, +} +DOT_PRIORITY: Final = 1 + + +class BracketMatchError(Exception): + """Raised when an opening bracket is unable to be matched to a closing bracket.""" + + +@dataclass +class BracketTracker: + """Keeps track of brackets on a line.""" + + depth: int = 0 + bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = field(default_factory=dict) + delimiters: Dict[LeafID, Priority] = field(default_factory=dict) + previous: Optional[Leaf] = None + _for_loop_depths: List[int] = field(default_factory=list) + _lambda_argument_depths: List[int] = field(default_factory=list) + invisible: List[Leaf] = field(default_factory=list) + + def mark(self, leaf: Leaf) -> None: + """Mark `leaf` with bracket-related metadata. Keep track of delimiters. + + All leaves receive an int `bracket_depth` field that stores how deep + within brackets a given leaf is. 0 means there are no enclosing brackets + that started on this line. + + If a leaf is itself a closing bracket and there is a matching opening + bracket earlier, it receives an `opening_bracket` field with which it forms a + pair. This is a one-directional link to avoid reference cycles. Closing + bracket without opening happens on lines continued from previous + breaks, e.g. `) -> "ReturnType":` as part of a funcdef where we place + the return type annotation on its own line of the previous closing RPAR. + + If a leaf is a delimiter (a token on which Black can split the line if + needed) and it's on depth 0, its `id()` is stored in the tracker's + `delimiters` field. + """ + if leaf.type == token.COMMENT: + return + + if ( + self.depth == 0 + and leaf.type in CLOSING_BRACKETS + and (self.depth, leaf.type) not in self.bracket_match + ): + return + + self.maybe_decrement_after_for_loop_variable(leaf) + self.maybe_decrement_after_lambda_arguments(leaf) + if leaf.type in CLOSING_BRACKETS: + self.depth -= 1 + try: + opening_bracket = self.bracket_match.pop((self.depth, leaf.type)) + except KeyError as e: + raise BracketMatchError( + "Unable to match a closing bracket to the following opening" + f" bracket: {leaf}" + ) from e + leaf.opening_bracket = opening_bracket + if not leaf.value: + self.invisible.append(leaf) + leaf.bracket_depth = self.depth + if self.depth == 0: + delim = is_split_before_delimiter(leaf, self.previous) + if delim and self.previous is not None: + self.delimiters[id(self.previous)] = delim + else: + delim = is_split_after_delimiter(leaf, self.previous) + if delim: + self.delimiters[id(leaf)] = delim + if leaf.type in OPENING_BRACKETS: + self.bracket_match[self.depth, BRACKET[leaf.type]] = leaf + self.depth += 1 + if not leaf.value: + self.invisible.append(leaf) + self.previous = leaf + self.maybe_increment_lambda_arguments(leaf) + self.maybe_increment_for_loop_variable(leaf) + + def any_open_brackets(self) -> bool: + """Return True if there is an yet unmatched open bracket on the line.""" + return bool(self.bracket_match) + + def max_delimiter_priority(self, exclude: Iterable[LeafID] = ()) -> Priority: + """Return the highest priority of a delimiter found on the line. + + Values are consistent with what `is_split_*_delimiter()` return. + Raises ValueError on no delimiters. + """ + return max(v for k, v in self.delimiters.items() if k not in exclude) + + def delimiter_count_with_priority(self, priority: Priority = 0) -> int: + """Return the number of delimiters with the given `priority`. + + If no `priority` is passed, defaults to max priority on the line. + """ + if not self.delimiters: + return 0 + + priority = priority or self.max_delimiter_priority() + return sum(1 for p in self.delimiters.values() if p == priority) + + def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool: + """In a for loop, or comprehension, the variables are often unpacks. + + To avoid splitting on the comma in this situation, increase the depth of + tokens between `for` and `in`. + """ + if leaf.type == token.NAME and leaf.value == "for": + self.depth += 1 + self._for_loop_depths.append(self.depth) + return True + + return False + + def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool: + """See `maybe_increment_for_loop_variable` above for explanation.""" + if ( + self._for_loop_depths + and self._for_loop_depths[-1] == self.depth + and leaf.type == token.NAME + and leaf.value == "in" + ): + self.depth -= 1 + self._for_loop_depths.pop() + return True + + return False + + def maybe_increment_lambda_arguments(self, leaf: Leaf) -> bool: + """In a lambda expression, there might be more than one argument. + + To avoid splitting on the comma in this situation, increase the depth of + tokens between `lambda` and `:`. + """ + if leaf.type == token.NAME and leaf.value == "lambda": + self.depth += 1 + self._lambda_argument_depths.append(self.depth) + return True + + return False + + def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool: + """See `maybe_increment_lambda_arguments` above for explanation.""" + if ( + self._lambda_argument_depths + and self._lambda_argument_depths[-1] == self.depth + and leaf.type == token.COLON + ): + self.depth -= 1 + self._lambda_argument_depths.pop() + return True + + return False + + def get_open_lsqb(self) -> Optional[Leaf]: + """Return the most recent opening square bracket (if any).""" + return self.bracket_match.get((self.depth - 1, token.RSQB)) + + +def is_split_after_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Priority: + """Return the priority of the `leaf` delimiter, given a line break after it. + + The delimiter priorities returned here are from those delimiters that would + cause a line break after themselves. + + Higher numbers are higher priority. + """ + if leaf.type == token.COMMA: + return COMMA_PRIORITY + + return 0 + + +def is_split_before_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Priority: + """Return the priority of the `leaf` delimiter, given a line break before it. + + The delimiter priorities returned here are from those delimiters that would + cause a line break before themselves. + + Higher numbers are higher priority. + """ + if is_vararg(leaf, within=VARARGS_PARENTS | UNPACKING_PARENTS): + # * and ** might also be MATH_OPERATORS but in this case they are not. + # Don't treat them as a delimiter. + return 0 + + if ( + leaf.type == token.DOT + and leaf.parent + and leaf.parent.type not in {syms.import_from, syms.dotted_name} + and (previous is None or previous.type in CLOSING_BRACKETS) + ): + return DOT_PRIORITY + + if ( + leaf.type in MATH_OPERATORS + and leaf.parent + and leaf.parent.type not in {syms.factor, syms.star_expr} + ): + return MATH_PRIORITIES[leaf.type] + + if leaf.type in COMPARATORS: + return COMPARATOR_PRIORITY + + if ( + leaf.type == token.STRING + and previous is not None + and previous.type == token.STRING + ): + return STRING_PRIORITY + + if leaf.type not in {token.NAME, token.ASYNC}: + return 0 + + if ( + leaf.value == "for" + and leaf.parent + and leaf.parent.type in {syms.comp_for, syms.old_comp_for} + or leaf.type == token.ASYNC + ): + if ( + not isinstance(leaf.prev_sibling, Leaf) + or leaf.prev_sibling.value != "async" + ): + return COMPREHENSION_PRIORITY + + if ( + leaf.value == "if" + and leaf.parent + and leaf.parent.type in {syms.comp_if, syms.old_comp_if} + ): + return COMPREHENSION_PRIORITY + + if leaf.value in {"if", "else"} and leaf.parent and leaf.parent.type == syms.test: + return TERNARY_PRIORITY + + if leaf.value == "is": + return COMPARATOR_PRIORITY + + if ( + leaf.value == "in" + and leaf.parent + and leaf.parent.type in {syms.comp_op, syms.comparison} + and not ( + previous is not None + and previous.type == token.NAME + and previous.value == "not" + ) + ): + return COMPARATOR_PRIORITY + + if ( + leaf.value == "not" + and leaf.parent + and leaf.parent.type == syms.comp_op + and not ( + previous is not None + and previous.type == token.NAME + and previous.value == "is" + ) + ): + return COMPARATOR_PRIORITY + + if leaf.value in LOGIC_OPERATORS and leaf.parent: + return LOGIC_PRIORITY + + return 0 + + +def max_delimiter_priority_in_atom(node: LN) -> Priority: + """Return maximum delimiter priority inside `node`. + + This is specific to atoms with contents contained in a pair of parentheses. + If `node` isn't an atom or there are no enclosing parentheses, returns 0. + """ + if node.type != syms.atom: + return 0 + + first = node.children[0] + last = node.children[-1] + if not (first.type == token.LPAR and last.type == token.RPAR): + return 0 + + bt = BracketTracker() + for c in node.children[1:-1]: + if isinstance(c, Leaf): + bt.mark(c) + else: + for leaf in c.leaves(): + bt.mark(leaf) + try: + return bt.max_delimiter_priority() + + except ValueError: + return 0 + + +def get_leaves_inside_matching_brackets(leaves: Sequence[Leaf]) -> Set[LeafID]: + """Return leaves that are inside matching brackets. + + The input `leaves` can have non-matching brackets at the head or tail parts. + Matching brackets are included. + """ + try: + # Start with the first opening bracket and ignore closing brackets before. + start_index = next( + i for i, l in enumerate(leaves) if l.type in OPENING_BRACKETS + ) + except StopIteration: + return set() + bracket_stack = [] + ids = set() + for i in range(start_index, len(leaves)): + leaf = leaves[i] + if leaf.type in OPENING_BRACKETS: + bracket_stack.append((BRACKET[leaf.type], i)) + if leaf.type in CLOSING_BRACKETS: + if bracket_stack and leaf.type == bracket_stack[-1][0]: + _, start = bracket_stack.pop() + for j in range(start, i + 1): + ids.add(id(leaves[j])) + else: + break + return ids diff --git a/.venv/lib/python3.11/site-packages/black/cache.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/cache.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..e966d69608da4074f25ad60975a1e0d14fffdb08 GIT binary patch literal 8256 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 O+6QTXw^@;>YX2Wuw_a`l literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/cache.py b/.venv/lib/python3.11/site-packages/black/cache.py new file mode 100644 index 00000000..9455ff44 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/cache.py @@ -0,0 +1,97 @@ +"""Caching of formatted files with feature-based invalidation.""" + +import os +import pickle +import tempfile +from pathlib import Path +from typing import Dict, Iterable, Set, Tuple + +from platformdirs import user_cache_dir + +from _black_version import version as __version__ +from black.mode import Mode + +# types +Timestamp = float +FileSize = int +CacheInfo = Tuple[Timestamp, FileSize] +Cache = Dict[str, CacheInfo] + + +def get_cache_dir() -> Path: + """Get the cache directory used by black. + + Users can customize this directory on all systems using `BLACK_CACHE_DIR` + environment variable. By default, the cache directory is the user cache directory + under the black application. + + This result is immediately set to a constant `black.cache.CACHE_DIR` as to avoid + repeated calls. + """ + # NOTE: Function mostly exists as a clean way to test getting the cache directory. + default_cache_dir = user_cache_dir("black", version=__version__) + cache_dir = Path(os.environ.get("BLACK_CACHE_DIR", default_cache_dir)) + return cache_dir + + +CACHE_DIR = get_cache_dir() + + +def read_cache(mode: Mode) -> Cache: + """Read the cache if it exists and is well formed. + + If it is not well formed, the call to write_cache later should resolve the issue. + """ + cache_file = get_cache_file(mode) + if not cache_file.exists(): + return {} + + with cache_file.open("rb") as fobj: + try: + cache: Cache = pickle.load(fobj) + except (pickle.UnpicklingError, ValueError, IndexError): + return {} + + return cache + + +def get_cache_file(mode: Mode) -> Path: + return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle" + + +def get_cache_info(path: Path) -> CacheInfo: + """Return the information used to check if a file is already formatted or not.""" + stat = path.stat() + return stat.st_mtime, stat.st_size + + +def filter_cached(cache: Cache, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]]: + """Split an iterable of paths in `sources` into two sets. + + The first contains paths of files that modified on disk or are not in the + cache. The other contains paths to non-modified files. + """ + todo, done = set(), set() + for src in sources: + res_src = src.resolve() + if cache.get(str(res_src)) != get_cache_info(res_src): + todo.add(src) + else: + done.add(src) + return todo, done + + +def write_cache(cache: Cache, sources: Iterable[Path], mode: Mode) -> None: + """Update the cache file.""" + cache_file = get_cache_file(mode) + try: + CACHE_DIR.mkdir(parents=True, exist_ok=True) + new_cache = { + **cache, + **{str(src.resolve()): get_cache_info(src) for src in sources}, + } + with tempfile.NamedTemporaryFile(dir=str(cache_file.parent), delete=False) as f: + pickle.dump(new_cache, f, protocol=4) + os.replace(f.name, cache_file) + except OSError: + pass diff --git a/.venv/lib/python3.11/site-packages/black/comments.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/comments.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..d1a0b999abe54bf14882e1699e1e6a63987a0137 GIT binary patch literal 8256 zcmeHMYit}>6~61W<4xnPx1niF5|I%k;GkygtRHa+rt8Pr<6yhSu_;uT$?VSBUa}9f zJDb{}fTc8TToe&S0s#_|38F&%6;+}z2v@0@%5_3&U{mB%Bvc*Um#a*La6Bt(MH2dM%iBsxSbymyJ6 zQnzMZR91V(XG2VdP$YnDU=wTiN*1UhD@?U28y_LccF3Y#gWr}n_!U`Unx~;KrRGI7 zXyJ2(S7sLwoI&>bpGglqPlxCVq4(B z<>(Io;f-Iue(s5@U;f;#pPzZlf8%#=`Y$ZM&|lks{uK~cEV}?zDoB0>7>~^<{?-OK z_DgXpQvF$=mHZfhE2QHj9UI`IfPdJjNcC5N2E+p*6i`Z}%M#x%G#CCS!rl7V$E`5K zifi#7ka(S_;|&QsmK;1=Vdj@=B`Y4Xi8w|0Sr;yJeJYtw=~>IjSh_Cscq(oQeG-V! z2gZhUGnR=>#j{o{Gd9$dNT*_BMkEolWtAm*bk;xzMk0O!6b~QQN6AA^!pLT0Surv< z0Ile{Yz!JDKAKJ@V<`*eJ;qEnmx$>D$(eMYIG+J1TXEpdEpgLcVbnG7!g=^S8=*MmSh(rIy zHLtdCQG4;m!vn{aqM54D2IX!I$%C3oozy0Fg7DUjgZXTPr^# zx%aWcwFCLbpd0fb5NO{Cv?yP;aW)jZ51eX?f_NX|}h<8f7Z2|^^zoKG@;FEPUU>2_ zEO~}@`Q=f7_!r<=ap3^nEq7<&?hM?Wf&a@H;P{8*9i9Y^dpNeaLV`M?9M{sdtr*Yp z^^oJNouuz0x`QakTX?2pVfnw`Eu@jZNey#sa)E*xj-8q*__+s_q1>{Gyt`nvfp{{5 zfKZVL$4s*%=kE>1Q&_g!MEsZgw@UTlHch|lQha;a{T=1r;;FE%joYSL zyj567>>8ePo(lcP^Q{U_nMK#S;5mo*k_%S}cb~5qqk7=9;k*HF-y;$)pU)HIhtF$Z zWqAtT9=PrDc}uYP5mYLTYoqc0M*682Yp!+wv&1W}VLk*3%FA5Mm)WN{`Vzb zo<}+lc%}T$^Ms$J4-w~uE|MSKKiq}i0$!;<*Ehi50bE|3*a6W7_)u2LUx{eO%38U} zNi_blXhp z9R&!|YwVzZ@R9DWLH&`wz9Zo=eXOf{Fbr4j4H9dZFS}Ec zt|Y-$A;~BE`#v&+p^!5(XXhVe&tnFCcbX_-z`W7D)(rcJPA&bF}k zmuka9+yuF zAB!k{XKq0($@U!gFb$C=$C<8U9{!VK=l$89<0Gc?a86*scCpI#9QTF*L*H!AaTHUI z&p|~Na>uODpoXzB+jG3dR3T07KkG3a2RX*x%yV4ElHfhpfN&>sH>;I>CO&IyMPe7|B^yf=Xk7VN9DWPx$ohsY7rqfTY0bAFuZ z*If3CWY0A2S}?X_w@U}*w#Dji}5r%g~EC^x6U(u5d=6F z+&;(MzB|~nzn5M19G?f?r~U829=8m)&v88ee>P7G#q-Yn=6&>7Sh3%OB9bl|;|2eJ-kv;3N8y*MqH=%;4X*#40 zije2M>r`E&_D@qj0?&a5;o<)9`HR0V?z_{z5f2+EA96Y8_HC&UaO0?G-$xC+&5A_b G_WuE(_+;k* literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/comments.py b/.venv/lib/python3.11/site-packages/black/comments.py new file mode 100644 index 00000000..7cf15bf6 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/comments.py @@ -0,0 +1,334 @@ +import re +import sys +from dataclasses import dataclass +from functools import lru_cache +from typing import Iterator, List, Optional, Union + +if sys.version_info >= (3, 8): + from typing import Final +else: + from typing_extensions import Final + +from black.nodes import ( + CLOSING_BRACKETS, + STANDALONE_COMMENT, + WHITESPACE, + container_of, + first_leaf_of, + preceding_leaf, + syms, +) +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + +# types +LN = Union[Leaf, Node] + +FMT_OFF: Final = {"# fmt: off", "# fmt:off", "# yapf: disable"} +FMT_SKIP: Final = {"# fmt: skip", "# fmt:skip"} +FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP} +FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"} + +COMMENT_EXCEPTIONS = " !:#'" + + +@dataclass +class ProtoComment: + """Describes a piece of syntax that is a comment. + + It's not a :class:`blib2to3.pytree.Leaf` so that: + + * it can be cached (`Leaf` objects should not be reused more than once as + they store their lineno, column, prefix, and parent information); + * `newlines` and `consumed` fields are kept separate from the `value`. This + simplifies handling of special marker comments like ``# fmt: off/on``. + """ + + type: int # token.COMMENT or STANDALONE_COMMENT + value: str # content of the comment + newlines: int # how many newlines before the comment + consumed: int # how many characters of the original leaf's prefix did we consume + + +def generate_comments(leaf: LN) -> Iterator[Leaf]: + """Clean the prefix of the `leaf` and generate comments from it, if any. + + Comments in lib2to3 are shoved into the whitespace prefix. This happens + in `pgen2/driver.py:Driver.parse_tokens()`. This was a brilliant implementation + move because it does away with modifying the grammar to include all the + possible places in which comments can be placed. + + The sad consequence for us though is that comments don't "belong" anywhere. + This is why this function generates simple parentless Leaf objects for + comments. We simply don't know what the correct parent should be. + + No matter though, we can live without this. We really only need to + differentiate between inline and standalone comments. The latter don't + share the line with any code. + + Inline comments are emitted as regular token.COMMENT leaves. Standalone + are emitted with a fake STANDALONE_COMMENT token identifier. + """ + for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER): + yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines) + + +@lru_cache(maxsize=4096) +def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]: + """Return a list of :class:`ProtoComment` objects parsed from the given `prefix`.""" + result: List[ProtoComment] = [] + if not prefix or "#" not in prefix: + return result + + consumed = 0 + nlines = 0 + ignored_lines = 0 + for index, line in enumerate(re.split("\r?\n", prefix)): + consumed += len(line) + 1 # adding the length of the split '\n' + line = line.lstrip() + if not line: + nlines += 1 + if not line.startswith("#"): + # Escaped newlines outside of a comment are not really newlines at + # all. We treat a single-line comment following an escaped newline + # as a simple trailing comment. + if line.endswith("\\"): + ignored_lines += 1 + continue + + if index == ignored_lines and not is_endmarker: + comment_type = token.COMMENT # simple trailing comment + else: + comment_type = STANDALONE_COMMENT + comment = make_comment(line) + result.append( + ProtoComment( + type=comment_type, value=comment, newlines=nlines, consumed=consumed + ) + ) + nlines = 0 + return result + + +def make_comment(content: str) -> str: + """Return a consistently formatted comment from the given `content` string. + + All comments (except for "##", "#!", "#:", '#'") should have a single + space between the hash sign and the content. + + If `content` didn't start with a hash sign, one is provided. + """ + content = content.rstrip() + if not content: + return "#" + + if content[0] == "#": + content = content[1:] + NON_BREAKING_SPACE = " " + if ( + content + and content[0] == NON_BREAKING_SPACE + and not content.lstrip().startswith("type:") + ): + content = " " + content[1:] # Replace NBSP by a simple space + if content and content[0] not in COMMENT_EXCEPTIONS: + content = " " + content + return "#" + content + + +def normalize_fmt_off(node: Node) -> None: + """Convert content between `# fmt: off`/`# fmt: on` into standalone comments.""" + try_again = True + while try_again: + try_again = convert_one_fmt_off_pair(node) + + +def convert_one_fmt_off_pair(node: Node) -> bool: + """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment. + + Returns True if a pair was converted. + """ + for leaf in node.leaves(): + previous_consumed = 0 + for comment in list_comments(leaf.prefix, is_endmarker=False): + if comment.value not in FMT_PASS: + previous_consumed = comment.consumed + continue + # We only want standalone comments. If there's no previous leaf or + # the previous leaf is indentation, it's a standalone comment in + # disguise. + if comment.value in FMT_PASS and comment.type != STANDALONE_COMMENT: + prev = preceding_leaf(leaf) + if prev: + if comment.value in FMT_OFF and prev.type not in WHITESPACE: + continue + if comment.value in FMT_SKIP and prev.type in WHITESPACE: + continue + + ignored_nodes = list(generate_ignored_nodes(leaf, comment)) + if not ignored_nodes: + continue + + first = ignored_nodes[0] # Can be a container node with the `leaf`. + parent = first.parent + prefix = first.prefix + if comment.value in FMT_OFF: + first.prefix = prefix[comment.consumed :] + if comment.value in FMT_SKIP: + first.prefix = "" + standalone_comment_prefix = prefix + else: + standalone_comment_prefix = ( + prefix[:previous_consumed] + "\n" * comment.newlines + ) + hidden_value = "".join(str(n) for n in ignored_nodes) + if comment.value in FMT_OFF: + hidden_value = comment.value + "\n" + hidden_value + if comment.value in FMT_SKIP: + hidden_value += " " + comment.value + if hidden_value.endswith("\n"): + # That happens when one of the `ignored_nodes` ended with a NEWLINE + # leaf (possibly followed by a DEDENT). + hidden_value = hidden_value[:-1] + first_idx: Optional[int] = None + for ignored in ignored_nodes: + index = ignored.remove() + if first_idx is None: + first_idx = index + assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)" + assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)" + parent.insert_child( + first_idx, + Leaf( + STANDALONE_COMMENT, + hidden_value, + prefix=standalone_comment_prefix, + ), + ) + return True + + return False + + +def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]: + """Starting from the container of `leaf`, generate all leaves until `# fmt: on`. + + If comment is skip, returns leaf only. + Stops at the end of the block. + """ + if comment.value in FMT_SKIP: + yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment) + return + container: Optional[LN] = container_of(leaf) + while container is not None and container.type != token.ENDMARKER: + if is_fmt_on(container): + return + + # fix for fmt: on in children + if children_contains_fmt_on(container): + for index, child in enumerate(container.children): + if isinstance(child, Leaf) and is_fmt_on(child): + if child.type in CLOSING_BRACKETS: + # This means `# fmt: on` is placed at a different bracket level + # than `# fmt: off`. This is an invalid use, but as a courtesy, + # we include this closing bracket in the ignored nodes. + # The alternative is to fail the formatting. + yield child + return + if ( + child.type == token.INDENT + and index < len(container.children) - 1 + and children_contains_fmt_on(container.children[index + 1]) + ): + # This means `# fmt: on` is placed right after an indentation + # level, and we shouldn't swallow the previous INDENT token. + return + if children_contains_fmt_on(child): + return + yield child + else: + if container.type == token.DEDENT and container.next_sibling is None: + # This can happen when there is no matching `# fmt: on` comment at the + # same level as `# fmt: on`. We need to keep this DEDENT. + return + yield container + container = container.next_sibling + + +def _generate_ignored_nodes_from_fmt_skip( + leaf: Leaf, comment: ProtoComment +) -> Iterator[LN]: + """Generate all leaves that should be ignored by the `# fmt: skip` from `leaf`.""" + prev_sibling = leaf.prev_sibling + parent = leaf.parent + # Need to properly format the leaf prefix to compare it to comment.value, + # which is also formatted + comments = list_comments(leaf.prefix, is_endmarker=False) + if not comments or comment.value != comments[0].value: + return + if prev_sibling is not None: + leaf.prefix = "" + siblings = [prev_sibling] + while "\n" not in prev_sibling.prefix and prev_sibling.prev_sibling is not None: + prev_sibling = prev_sibling.prev_sibling + siblings.insert(0, prev_sibling) + yield from siblings + elif ( + parent is not None and parent.type == syms.suite and leaf.type == token.NEWLINE + ): + # The `# fmt: skip` is on the colon line of the if/while/def/class/... + # statements. The ignored nodes should be previous siblings of the + # parent suite node. + leaf.prefix = "" + ignored_nodes: List[LN] = [] + parent_sibling = parent.prev_sibling + while parent_sibling is not None and parent_sibling.type != syms.suite: + ignored_nodes.insert(0, parent_sibling) + parent_sibling = parent_sibling.prev_sibling + # Special case for `async_stmt` where the ASYNC token is on the + # grandparent node. + grandparent = parent.parent + if ( + grandparent is not None + and grandparent.prev_sibling is not None + and grandparent.prev_sibling.type == token.ASYNC + ): + ignored_nodes.insert(0, grandparent.prev_sibling) + yield from iter(ignored_nodes) + + +def is_fmt_on(container: LN) -> bool: + """Determine whether formatting is switched on within a container. + Determined by whether the last `# fmt:` comment is `on` or `off`. + """ + fmt_on = False + for comment in list_comments(container.prefix, is_endmarker=False): + if comment.value in FMT_ON: + fmt_on = True + elif comment.value in FMT_OFF: + fmt_on = False + return fmt_on + + +def children_contains_fmt_on(container: LN) -> bool: + """Determine if children have formatting switched on.""" + for child in container.children: + leaf = first_leaf_of(child) + if leaf is not None and is_fmt_on(leaf): + return True + + return False + + +def contains_pragma_comment(comment_list: List[Leaf]) -> bool: + """ + Returns: + True iff one of the comments in @comment_list is a pragma used by one + of the more common static analysis tools for python (e.g. mypy, flake8, + pylint). + """ + for comment in comment_list: + if comment.value.startswith(("# type:", "# noqa", "# pylint:")): + return True + + return False diff --git a/.venv/lib/python3.11/site-packages/black/concurrency.py b/.venv/lib/python3.11/site-packages/black/concurrency.py new file mode 100644 index 00000000..1598f51e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/concurrency.py @@ -0,0 +1,187 @@ +""" +Formatting many files at once via multiprocessing. Contains entrypoint and utilities. + +NOTE: this module is only imported if we need to format several files at once. +""" + +import asyncio +import logging +import os +import signal +import sys +from concurrent.futures import Executor, ProcessPoolExecutor, ThreadPoolExecutor +from multiprocessing import Manager +from pathlib import Path +from typing import Any, Iterable, Optional, Set + +from mypy_extensions import mypyc_attr + +from black import WriteBack, format_file_in_place +from black.cache import Cache, filter_cached, read_cache, write_cache +from black.mode import Mode +from black.output import err +from black.report import Changed, Report + + +def maybe_install_uvloop() -> None: + """If our environment has uvloop installed we use it. + + This is called only from command-line entry points to avoid + interfering with the parent process if Black is used as a library. + """ + try: + import uvloop + + uvloop.install() + except ImportError: + pass + + +def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None: + """asyncio signal handler that cancels all `tasks` and reports to stderr.""" + err("Aborted!") + for task in tasks: + task.cancel() + + +def shutdown(loop: asyncio.AbstractEventLoop) -> None: + """Cancel all pending tasks on `loop`, wait for them, and close the loop.""" + try: + # This part is borrowed from asyncio/runners.py in Python 3.7b2. + to_cancel = [task for task in asyncio.all_tasks(loop) if not task.done()] + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) + finally: + # `concurrent.futures.Future` objects cannot be cancelled once they + # are already running. There might be some when the `shutdown()` happened. + # Silence their logger's spew about the event loop being closed. + cf_logger = logging.getLogger("concurrent.futures") + cf_logger.setLevel(logging.CRITICAL) + loop.close() + + +# diff-shades depends on being to monkeypatch this function to operate. I know it's +# not ideal, but this shouldn't cause any issues ... hopefully. ~ichard26 +@mypyc_attr(patchable=True) +def reformat_many( + sources: Set[Path], + fast: bool, + write_back: WriteBack, + mode: Mode, + report: Report, + workers: Optional[int], +) -> None: + """Reformat multiple files using a ProcessPoolExecutor.""" + maybe_install_uvloop() + + executor: Executor + if workers is None: + workers = os.cpu_count() or 1 + if sys.platform == "win32": + # Work around https://bugs.python.org/issue26903 + workers = min(workers, 60) + try: + executor = ProcessPoolExecutor(max_workers=workers) + except (ImportError, NotImplementedError, OSError): + # we arrive here if the underlying system does not support multi-processing + # like in AWS Lambda or Termux, in which case we gracefully fallback to + # a ThreadPoolExecutor with just a single worker (more workers would not do us + # any good due to the Global Interpreter Lock) + executor = ThreadPoolExecutor(max_workers=1) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + loop.run_until_complete( + schedule_formatting( + sources=sources, + fast=fast, + write_back=write_back, + mode=mode, + report=report, + loop=loop, + executor=executor, + ) + ) + finally: + try: + shutdown(loop) + finally: + asyncio.set_event_loop(None) + if executor is not None: + executor.shutdown() + + +async def schedule_formatting( + sources: Set[Path], + fast: bool, + write_back: WriteBack, + mode: Mode, + report: "Report", + loop: asyncio.AbstractEventLoop, + executor: "Executor", +) -> None: + """Run formatting of `sources` in parallel using the provided `executor`. + + (Use ProcessPoolExecutors for actual parallelism.) + + `write_back`, `fast`, and `mode` options are passed to + :func:`format_file_in_place`. + """ + cache: Cache = {} + if write_back not in (WriteBack.DIFF, WriteBack.COLOR_DIFF): + cache = read_cache(mode) + sources, cached = filter_cached(cache, sources) + for src in sorted(cached): + report.done(src, Changed.CACHED) + if not sources: + return + + cancelled = [] + sources_to_cache = [] + lock = None + if write_back in (WriteBack.DIFF, WriteBack.COLOR_DIFF): + # For diff output, we need locks to ensure we don't interleave output + # from different processes. + manager = Manager() + lock = manager.Lock() + tasks = { + asyncio.ensure_future( + loop.run_in_executor( + executor, format_file_in_place, src, fast, mode, write_back, lock + ) + ): src + for src in sorted(sources) + } + pending = tasks.keys() + try: + loop.add_signal_handler(signal.SIGINT, cancel, pending) + loop.add_signal_handler(signal.SIGTERM, cancel, pending) + except NotImplementedError: + # There are no good alternatives for these on Windows. + pass + while pending: + done, _ = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) + for task in done: + src = tasks.pop(task) + if task.cancelled(): + cancelled.append(task) + elif task.exception(): + report.failed(src, str(task.exception())) + else: + changed = Changed.YES if task.result() else Changed.NO + # If the file was written back or was successfully checked as + # well-formatted, store this information in the cache. + if write_back is WriteBack.YES or ( + write_back is WriteBack.CHECK and changed is Changed.NO + ): + sources_to_cache.append(src) + report.done(src, changed) + if cancelled: + await asyncio.gather(*cancelled, return_exceptions=True) + if sources_to_cache: + write_cache(cache, sources_to_cache, mode) diff --git a/.venv/lib/python3.11/site-packages/black/const.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/const.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..2143ca08f8856efa1fa0b8dc415083edc2e6a170 GIT binary patch literal 8256 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 Lyv2${Rr~({{Fz^g literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/const.py b/.venv/lib/python3.11/site-packages/black/const.py new file mode 100644 index 00000000..0e13f315 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/const.py @@ -0,0 +1,4 @@ +DEFAULT_LINE_LENGTH = 88 +DEFAULT_EXCLUDES = r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|venv|\.svn|\.ipynb_checkpoints|_build|buck-out|build|dist|__pypackages__)/" # noqa: B950 +DEFAULT_INCLUDES = r"(\.pyi?|\.ipynb)$" +STDIN_PLACEHOLDER = "__BLACK_STDIN_FILENAME__" diff --git a/.venv/lib/python3.11/site-packages/black/debug.py b/.venv/lib/python3.11/site-packages/black/debug.py new file mode 100644 index 00000000..150b4484 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/debug.py @@ -0,0 +1,47 @@ +from dataclasses import dataclass +from typing import Iterator, TypeVar, Union + +from black.nodes import Visitor +from black.output import out +from black.parsing import lib2to3_parse +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node, type_repr + +LN = Union[Leaf, Node] +T = TypeVar("T") + + +@dataclass +class DebugVisitor(Visitor[T]): + tree_depth: int = 0 + + def visit_default(self, node: LN) -> Iterator[T]: + indent = " " * (2 * self.tree_depth) + if isinstance(node, Node): + _type = type_repr(node.type) + out(f"{indent}{_type}", fg="yellow") + self.tree_depth += 1 + for child in node.children: + yield from self.visit(child) + + self.tree_depth -= 1 + out(f"{indent}/{_type}", fg="yellow", bold=False) + else: + _type = token.tok_name.get(node.type, str(node.type)) + out(f"{indent}{_type}", fg="blue", nl=False) + if node.prefix: + # We don't have to handle prefixes for `Node` objects since + # that delegates to the first child anyway. + out(f" {node.prefix!r}", fg="green", bold=False, nl=False) + out(f" {node.value!r}", fg="blue", bold=False) + + @classmethod + def show(cls, code: Union[str, Leaf, Node]) -> None: + """Pretty-print the lib2to3 AST of a given string of `code`. + + Convenience method for debugging. + """ + v: DebugVisitor[None] = DebugVisitor() + if isinstance(code, str): + code = lib2to3_parse(code) + list(v.visit(code)) diff --git a/.venv/lib/python3.11/site-packages/black/files.py b/.venv/lib/python3.11/site-packages/black/files.py new file mode 100644 index 00000000..8c013112 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/files.py @@ -0,0 +1,399 @@ +import io +import os +import sys +from functools import lru_cache +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterable, + Iterator, + List, + Optional, + Pattern, + Sequence, + Tuple, + Union, +) + +from mypy_extensions import mypyc_attr +from packaging.specifiers import InvalidSpecifier, Specifier, SpecifierSet +from packaging.version import InvalidVersion, Version +from pathspec import PathSpec +from pathspec.patterns.gitwildmatch import GitWildMatchPatternError + +if sys.version_info >= (3, 11): + try: + import tomllib + except ImportError: + # Help users on older alphas + if not TYPE_CHECKING: + import tomli as tomllib +else: + import tomli as tomllib + +from black.handle_ipynb_magics import jupyter_dependencies_are_installed +from black.mode import TargetVersion +from black.output import err +from black.report import Report + +if TYPE_CHECKING: + import colorama # noqa: F401 + + +@lru_cache() +def find_project_root( + srcs: Sequence[str], stdin_filename: Optional[str] = None +) -> Tuple[Path, str]: + """Return a directory containing .git, .hg, or pyproject.toml. + + That directory will be a common parent of all files and directories + passed in `srcs`. + + If no directory in the tree contains a marker that would specify it's the + project root, the root of the file system is returned. + + Returns a two-tuple with the first element as the project root path and + the second element as a string describing the method by which the + project root was discovered. + """ + if stdin_filename is not None: + srcs = tuple(stdin_filename if s == "-" else s for s in srcs) + if not srcs: + srcs = [str(Path.cwd().resolve())] + + path_srcs = [Path(Path.cwd(), src).resolve() for src in srcs] + + # A list of lists of parents for each 'src'. 'src' is included as a + # "parent" of itself if it is a directory + src_parents = [ + list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs + ] + + common_base = max( + set.intersection(*(set(parents) for parents in src_parents)), + key=lambda path: path.parts, + ) + + for directory in (common_base, *common_base.parents): + if (directory / ".git").exists(): + return directory, ".git directory" + + if (directory / ".hg").is_dir(): + return directory, ".hg directory" + + if (directory / "pyproject.toml").is_file(): + return directory, "pyproject.toml" + + return directory, "file system root" + + +def find_pyproject_toml(path_search_start: Tuple[str, ...]) -> Optional[str]: + """Find the absolute filepath to a pyproject.toml if it exists""" + path_project_root, _ = find_project_root(path_search_start) + path_pyproject_toml = path_project_root / "pyproject.toml" + if path_pyproject_toml.is_file(): + return str(path_pyproject_toml) + + try: + path_user_pyproject_toml = find_user_pyproject_toml() + return ( + str(path_user_pyproject_toml) + if path_user_pyproject_toml.is_file() + else None + ) + except (PermissionError, RuntimeError) as e: + # We do not have access to the user-level config directory, so ignore it. + err(f"Ignoring user configuration directory due to {e!r}") + return None + + +@mypyc_attr(patchable=True) +def parse_pyproject_toml(path_config: str) -> Dict[str, Any]: + """Parse a pyproject toml file, pulling out relevant parts for Black. + + If parsing fails, will raise a tomllib.TOMLDecodeError. + """ + with open(path_config, "rb") as f: + pyproject_toml = tomllib.load(f) + config: Dict[str, Any] = pyproject_toml.get("tool", {}).get("black", {}) + config = {k.replace("--", "").replace("-", "_"): v for k, v in config.items()} + + if "target_version" not in config: + inferred_target_version = infer_target_version(pyproject_toml) + if inferred_target_version is not None: + config["target_version"] = [v.name.lower() for v in inferred_target_version] + + return config + + +def infer_target_version( + pyproject_toml: Dict[str, Any] +) -> Optional[List[TargetVersion]]: + """Infer Black's target version from the project metadata in pyproject.toml. + + Supports the PyPA standard format (PEP 621): + https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#requires-python + + If the target version cannot be inferred, returns None. + """ + project_metadata = pyproject_toml.get("project", {}) + requires_python = project_metadata.get("requires-python", None) + if requires_python is not None: + try: + return parse_req_python_version(requires_python) + except InvalidVersion: + pass + try: + return parse_req_python_specifier(requires_python) + except (InvalidSpecifier, InvalidVersion): + pass + + return None + + +def parse_req_python_version(requires_python: str) -> Optional[List[TargetVersion]]: + """Parse a version string (i.e. ``"3.7"``) to a list of TargetVersion. + + If parsing fails, will raise a packaging.version.InvalidVersion error. + If the parsed version cannot be mapped to a valid TargetVersion, returns None. + """ + version = Version(requires_python) + if version.release[0] != 3: + return None + try: + return [TargetVersion(version.release[1])] + except (IndexError, ValueError): + return None + + +def parse_req_python_specifier(requires_python: str) -> Optional[List[TargetVersion]]: + """Parse a specifier string (i.e. ``">=3.7,<3.10"``) to a list of TargetVersion. + + If parsing fails, will raise a packaging.specifiers.InvalidSpecifier error. + If the parsed specifier cannot be mapped to a valid TargetVersion, returns None. + """ + specifier_set = strip_specifier_set(SpecifierSet(requires_python)) + if not specifier_set: + return None + + target_version_map = {f"3.{v.value}": v for v in TargetVersion} + compatible_versions: List[str] = list(specifier_set.filter(target_version_map)) + if compatible_versions: + return [target_version_map[v] for v in compatible_versions] + return None + + +def strip_specifier_set(specifier_set: SpecifierSet) -> SpecifierSet: + """Strip minor versions for some specifiers in the specifier set. + + For background on version specifiers, see PEP 440: + https://peps.python.org/pep-0440/#version-specifiers + """ + specifiers = [] + for s in specifier_set: + if "*" in str(s): + specifiers.append(s) + elif s.operator in ["~=", "==", ">=", "==="]: + version = Version(s.version) + stripped = Specifier(f"{s.operator}{version.major}.{version.minor}") + specifiers.append(stripped) + elif s.operator == ">": + version = Version(s.version) + if len(version.release) > 2: + s = Specifier(f">={version.major}.{version.minor}") + specifiers.append(s) + else: + specifiers.append(s) + + return SpecifierSet(",".join(str(s) for s in specifiers)) + + +@lru_cache() +def find_user_pyproject_toml() -> Path: + r"""Return the path to the top-level user configuration for black. + + This looks for ~\.black on Windows and ~/.config/black on Linux and other + Unix systems. + + May raise: + - RuntimeError: if the current user has no homedir + - PermissionError: if the current process cannot access the user's homedir + """ + if sys.platform == "win32": + # Windows + user_config_path = Path.home() / ".black" + else: + config_root = os.environ.get("XDG_CONFIG_HOME", "~/.config") + user_config_path = Path(config_root).expanduser() / "black" + return user_config_path.resolve() + + +@lru_cache() +def get_gitignore(root: Path) -> PathSpec: + """Return a PathSpec matching gitignore content if present.""" + gitignore = root / ".gitignore" + lines: List[str] = [] + if gitignore.is_file(): + with gitignore.open(encoding="utf-8") as gf: + lines = gf.readlines() + try: + return PathSpec.from_lines("gitwildmatch", lines) + except GitWildMatchPatternError as e: + err(f"Could not parse {gitignore}: {e}") + raise + + +def normalize_path_maybe_ignore( + path: Path, + root: Path, + report: Optional[Report] = None, +) -> Optional[str]: + """Normalize `path`. May return `None` if `path` was ignored. + + `report` is where "path ignored" output goes. + """ + try: + abspath = path if path.is_absolute() else Path.cwd() / path + normalized_path = abspath.resolve() + try: + root_relative_path = normalized_path.relative_to(root).as_posix() + except ValueError: + if report: + report.path_ignored( + path, f"is a symbolic link that points outside {root}" + ) + return None + + except OSError as e: + if report: + report.path_ignored(path, f"cannot be read because {e}") + return None + + return root_relative_path + + +def path_is_ignored( + path: Path, gitignore_dict: Dict[Path, PathSpec], report: Report +) -> bool: + for gitignore_path, pattern in gitignore_dict.items(): + relative_path = normalize_path_maybe_ignore(path, gitignore_path, report) + if relative_path is None: + break + if pattern.match_file(relative_path): + report.path_ignored(path, "matches a .gitignore file content") + return True + return False + + +def path_is_excluded( + normalized_path: str, + pattern: Optional[Pattern[str]], +) -> bool: + match = pattern.search(normalized_path) if pattern else None + return bool(match and match.group(0)) + + +def gen_python_files( + paths: Iterable[Path], + root: Path, + include: Pattern[str], + exclude: Pattern[str], + extend_exclude: Optional[Pattern[str]], + force_exclude: Optional[Pattern[str]], + report: Report, + gitignore_dict: Optional[Dict[Path, PathSpec]], + *, + verbose: bool, + quiet: bool, +) -> Iterator[Path]: + """Generate all files under `path` whose paths are not excluded by the + `exclude_regex`, `extend_exclude`, or `force_exclude` regexes, + but are included by the `include` regex. + + Symbolic links pointing outside of the `root` directory are ignored. + + `report` is where output about exclusions goes. + """ + + assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}" + for child in paths: + normalized_path = normalize_path_maybe_ignore(child, root, report) + if normalized_path is None: + continue + + # First ignore files matching .gitignore, if passed + if gitignore_dict and path_is_ignored(child, gitignore_dict, report): + continue + + # Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options. + normalized_path = "/" + normalized_path + if child.is_dir(): + normalized_path += "/" + + if path_is_excluded(normalized_path, exclude): + report.path_ignored(child, "matches the --exclude regular expression") + continue + + if path_is_excluded(normalized_path, extend_exclude): + report.path_ignored( + child, "matches the --extend-exclude regular expression" + ) + continue + + if path_is_excluded(normalized_path, force_exclude): + report.path_ignored(child, "matches the --force-exclude regular expression") + continue + + if child.is_dir(): + # If gitignore is None, gitignore usage is disabled, while a Falsey + # gitignore is when the directory doesn't have a .gitignore file. + if gitignore_dict is not None: + new_gitignore_dict = { + **gitignore_dict, + root / child: get_gitignore(child), + } + else: + new_gitignore_dict = None + yield from gen_python_files( + child.iterdir(), + root, + include, + exclude, + extend_exclude, + force_exclude, + report, + new_gitignore_dict, + verbose=verbose, + quiet=quiet, + ) + + elif child.is_file(): + if child.suffix == ".ipynb" and not jupyter_dependencies_are_installed( + verbose=verbose, quiet=quiet + ): + continue + include_match = include.search(normalized_path) if include else True + if include_match: + yield child + + +def wrap_stream_for_windows( + f: io.TextIOWrapper, +) -> Union[io.TextIOWrapper, "colorama.AnsiToWin32"]: + """ + Wrap stream with colorama's wrap_stream so colors are shown on Windows. + + If `colorama` is unavailable, the original stream is returned unmodified. + Otherwise, the `wrap_stream()` function determines whether the stream needs + to be wrapped for a Windows environment and will accordingly either return + an `AnsiToWin32` wrapper or the original stream. + """ + try: + from colorama.initialise import wrap_stream + except ImportError: + return f + else: + # Set `strip=False` to avoid needing to modify test_express_diff_with_color. + return wrap_stream(f, convert=None, strip=False, autoreset=False, wrap=True) diff --git a/.venv/lib/python3.11/site-packages/black/handle_ipynb_magics.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/handle_ipynb_magics.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..825e3f225f600ab96906326e5ac75f58dffec63c GIT binary patch literal 8280 zcmeHMT}&L;6~4=Yy~GYsw~4Vq@~E{NT-5{SFSr)X@;|uXP>fYO3LS=>1-98AvO8@yQ$b+*z=gv9H z>}5!zKJ=mTMYHFg@BG}~xpVH}$2|kRC4qq85)|JQ$Sv$}kuV9)?4t^huxJxI;kQ=Q zNZqnc(Uw|mxeGBBLX!ZtflX}OD_NkLtS~i7TzrTq+aZf~)fKM1x$y8IX2d3$&pG~pDEjY8|-KbrJv9GsUn48YHz!F3DJBorQyi2 zeF=O7;psbhTHO4Tb?42WF1&N>uV*^g~DOqvMCE`iK=M`L-=42w3G&7Esc1%;4v1H5<<^&L7_Kysj zb~GKGjAfi?dStLGo=QeXtnql%mHA7|$c%*!ta$7KD4sZH4wHwjxRuF7Gh!&$53QJ{ zYz!JDe#%PP@u(S_&Lzjqgf$t9plX*joyh{+pO{Xi9n_T-;s;YU2!%P6GrOZ!Jf4b( zcx*hPXHt5rFr&8RSTMx#OvW7{KuKHl4fJ<*nT>j*-a^ykAtkVvy??F zS}-pDK6K4(Dc|xki1LSi4ZoEK!caqg3WaMcc_@coLLR4TZAtP!M;?c;wkY{$k;ksD zElB<<^4R6IdEkx9Wmf<&F1P;#$Z6y7hB0@`xVTYqzGUF*G@M z%h?U>zqLaXtE14R)hYNHeWPq0#DRZbL#qkB^1u_&WdT#uJ`ak*`6_hoy!5~5>^}|m zmjDJ|`K8gd?|_5K1GA{V_ARJynqL_L%^w23`tBz%co^7MjC?2?GUl$YK8MKV_V0uA z=trx+AbIeS{L0bAN1z+CAP{KZ4zwWOw6|P)TtKjoDE5HLwZ^4w9dA1Jpl!D{M;jW( zTdawR#wOb|6S?VJM0|=&$Fs>Ck6V$);Oy`_E#RH02_8QPZQ+71-O1;}KsA_xDWGFO zXMtkDZyT1tg<%nxsS4EW-Wj?Ay9jOZ+w(NojsX=FRlOC}Cn|TH56y~)_8=*ZA`-WfliImxIulDJwI;n;Z`A4>Ycf+m+%wSAajM5`YOc3a zPU8`I9XYIaO5uYLaq~UiD+)qH%=@Sq4+`Et#dxXUeO8Q@30|*ae23t5F2;8X-Y>;? zsIae!@vZknln2E#a>KE&fU}lVw`zw3UO@kCV=ciL{l7nszpk$bpH6bF)!i)p{o}jnBD;NZaIN z54P5iXJc`@Ufw|KeeR!fEWN`WgFR-?$!^T1$&qp0&ZOwpD$MS)Cp!lFyFdfJb!LxY zx@JcAFhG!LV+VZ$4|jG9m=E{%p6VGfM>;wOdcf_cCFUsSBZ$_88J zTnl&CxfWdiUIf(86x#tWvQJhb7J+`Gz&rftvPsBT3LQ3h0yeGkO8r+Xepm2S=D3yP z9Hs{quM}1mQ2hRU1TiMtb3DW}Oqv{js-OKfkYh~E_8d1corQA+3$}|@w&!>_3>f-m zdycP|a@-CovXDDwg#k5;q1m3}Fs2%5a{pP6>1mK-OwK&VYfRZ*?LWB{HdIi?_8iwS zT_i^n)1?-fjEJofm%0o5MmI42xd@c$E-7VcBz2m9(R8DPBj98WSG^D0A~ z^W;o_q}VS|N0`Qx1!FsQ`-Ec8-&LkcxFP47ZINCg`$hsej%9k5a3v+{k;U03V3*baGb{f*HiVGXZi}*R(k9UaW*=I!g?;Z$us^s z2yiaAeU8UNpJC7bt}FH&w^w~m`@euaZW(T$<9q(U?IJA{&pY><_t6cdeZB`QkxRDwN7cAWh+cEtoz*y)0a~u{Xd)8w&JPzj9p@Qu;zpLOh z9nuCx$n)Oys?JmUPg9-(&w;PQ!~Nm&7vC@LyVt%B4;v`WigRw?l?nkj5nua9sezAK Ik*I3_ANogjV*mgE literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/handle_ipynb_magics.py b/.venv/lib/python3.11/site-packages/black/handle_ipynb_magics.py new file mode 100644 index 00000000..9e1af757 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/handle_ipynb_magics.py @@ -0,0 +1,459 @@ +"""Functions to process IPython magics with.""" + +import ast +import collections +import dataclasses +import secrets +import sys +from functools import lru_cache +from typing import Dict, List, Optional, Tuple + +if sys.version_info >= (3, 10): + from typing import TypeGuard +else: + from typing_extensions import TypeGuard + +from black.output import out +from black.report import NothingChanged + +TRANSFORMED_MAGICS = frozenset( + ( + "get_ipython().run_cell_magic", + "get_ipython().system", + "get_ipython().getoutput", + "get_ipython().run_line_magic", + ) +) +TOKENS_TO_IGNORE = frozenset( + ( + "ENDMARKER", + "NL", + "NEWLINE", + "COMMENT", + "DEDENT", + "UNIMPORTANT_WS", + "ESCAPED_NL", + ) +) +PYTHON_CELL_MAGICS = frozenset( + ( + "capture", + "prun", + "pypy", + "python", + "python3", + "time", + "timeit", + ) +) +TOKEN_HEX = secrets.token_hex + + +@dataclasses.dataclass(frozen=True) +class Replacement: + mask: str + src: str + + +@lru_cache() +def jupyter_dependencies_are_installed(*, verbose: bool, quiet: bool) -> bool: + try: + import IPython # noqa:F401 + import tokenize_rt # noqa:F401 + except ModuleNotFoundError: + if verbose or not quiet: + msg = ( + "Skipping .ipynb files as Jupyter dependencies are not installed.\n" + 'You can fix this by running ``pip install "black[jupyter]"``' + ) + out(msg) + return False + else: + return True + + +def remove_trailing_semicolon(src: str) -> Tuple[str, bool]: + """Remove trailing semicolon from Jupyter notebook cell. + + For example, + + fig, ax = plt.subplots() + ax.plot(x_data, y_data); # plot data + + would become + + fig, ax = plt.subplots() + ax.plot(x_data, y_data) # plot data + + Mirrors the logic in `quiet` from `IPython.core.displayhook`, but uses + ``tokenize_rt`` so that round-tripping works fine. + """ + from tokenize_rt import reversed_enumerate, src_to_tokens, tokens_to_src + + tokens = src_to_tokens(src) + trailing_semicolon = False + for idx, token in reversed_enumerate(tokens): + if token.name in TOKENS_TO_IGNORE: + continue + if token.name == "OP" and token.src == ";": + del tokens[idx] + trailing_semicolon = True + break + if not trailing_semicolon: + return src, False + return tokens_to_src(tokens), True + + +def put_trailing_semicolon_back(src: str, has_trailing_semicolon: bool) -> str: + """Put trailing semicolon back if cell originally had it. + + Mirrors the logic in `quiet` from `IPython.core.displayhook`, but uses + ``tokenize_rt`` so that round-tripping works fine. + """ + if not has_trailing_semicolon: + return src + from tokenize_rt import reversed_enumerate, src_to_tokens, tokens_to_src + + tokens = src_to_tokens(src) + for idx, token in reversed_enumerate(tokens): + if token.name in TOKENS_TO_IGNORE: + continue + tokens[idx] = token._replace(src=token.src + ";") + break + else: # pragma: nocover + raise AssertionError( + "INTERNAL ERROR: Was not able to reinstate trailing semicolon. " + "Please report a bug on https://github.com/psf/black/issues. " + ) from None + return str(tokens_to_src(tokens)) + + +def mask_cell(src: str) -> Tuple[str, List[Replacement]]: + """Mask IPython magics so content becomes parseable Python code. + + For example, + + %matplotlib inline + 'foo' + + becomes + + "25716f358c32750e" + 'foo' + + The replacements are returned, along with the transformed code. + """ + replacements: List[Replacement] = [] + try: + ast.parse(src) + except SyntaxError: + # Might have IPython magics, will process below. + pass + else: + # Syntax is fine, nothing to mask, early return. + return src, replacements + + from IPython.core.inputtransformer2 import TransformerManager + + transformer_manager = TransformerManager() + transformed = transformer_manager.transform_cell(src) + transformed, cell_magic_replacements = replace_cell_magics(transformed) + replacements += cell_magic_replacements + transformed = transformer_manager.transform_cell(transformed) + transformed, magic_replacements = replace_magics(transformed) + if len(transformed.splitlines()) != len(src.splitlines()): + # Multi-line magic, not supported. + raise NothingChanged + replacements += magic_replacements + return transformed, replacements + + +def get_token(src: str, magic: str) -> str: + """Return randomly generated token to mask IPython magic with. + + For example, if 'magic' was `%matplotlib inline`, then a possible + token to mask it with would be `"43fdd17f7e5ddc83"`. The token + will be the same length as the magic, and we make sure that it was + not already present anywhere else in the cell. + """ + assert magic + nbytes = max(len(magic) // 2 - 1, 1) + token = TOKEN_HEX(nbytes) + counter = 0 + while token in src: + token = TOKEN_HEX(nbytes) + counter += 1 + if counter > 100: + raise AssertionError( + "INTERNAL ERROR: Black was not able to replace IPython magic. " + "Please report a bug on https://github.com/psf/black/issues. " + f"The magic might be helpful: {magic}" + ) from None + if len(token) + 2 < len(magic): + token = f"{token}." + return f'"{token}"' + + +def replace_cell_magics(src: str) -> Tuple[str, List[Replacement]]: + """Replace cell magic with token. + + Note that 'src' will already have been processed by IPython's + TransformerManager().transform_cell. + + Example, + + get_ipython().run_cell_magic('t', '-n1', 'ls =!ls\\n') + + becomes + + "a794." + ls =!ls + + The replacement, along with the transformed code, is returned. + """ + replacements: List[Replacement] = [] + + tree = ast.parse(src) + + cell_magic_finder = CellMagicFinder() + cell_magic_finder.visit(tree) + if cell_magic_finder.cell_magic is None: + return src, replacements + header = cell_magic_finder.cell_magic.header + mask = get_token(src, header) + replacements.append(Replacement(mask=mask, src=header)) + return f"{mask}\n{cell_magic_finder.cell_magic.body}", replacements + + +def replace_magics(src: str) -> Tuple[str, List[Replacement]]: + """Replace magics within body of cell. + + Note that 'src' will already have been processed by IPython's + TransformerManager().transform_cell. + + Example, this + + get_ipython().run_line_magic('matplotlib', 'inline') + 'foo' + + becomes + + "5e67db56d490fd39" + 'foo' + + The replacement, along with the transformed code, are returned. + """ + replacements = [] + magic_finder = MagicFinder() + magic_finder.visit(ast.parse(src)) + new_srcs = [] + for i, line in enumerate(src.splitlines(), start=1): + if i in magic_finder.magics: + offsets_and_magics = magic_finder.magics[i] + if len(offsets_and_magics) != 1: # pragma: nocover + raise AssertionError( + f"Expecting one magic per line, got: {offsets_and_magics}\n" + "Please report a bug on https://github.com/psf/black/issues." + ) + col_offset, magic = ( + offsets_and_magics[0].col_offset, + offsets_and_magics[0].magic, + ) + mask = get_token(src, magic) + replacements.append(Replacement(mask=mask, src=magic)) + line = line[:col_offset] + mask + new_srcs.append(line) + return "\n".join(new_srcs), replacements + + +def unmask_cell(src: str, replacements: List[Replacement]) -> str: + """Remove replacements from cell. + + For example + + "9b20" + foo = bar + + becomes + + %%time + foo = bar + """ + for replacement in replacements: + src = src.replace(replacement.mask, replacement.src) + return src + + +def _is_ipython_magic(node: ast.expr) -> TypeGuard[ast.Attribute]: + """Check if attribute is IPython magic. + + Note that the source of the abstract syntax tree + will already have been processed by IPython's + TransformerManager().transform_cell. + """ + return ( + isinstance(node, ast.Attribute) + and isinstance(node.value, ast.Call) + and isinstance(node.value.func, ast.Name) + and node.value.func.id == "get_ipython" + ) + + +def _get_str_args(args: List[ast.expr]) -> List[str]: + str_args = [] + for arg in args: + assert isinstance(arg, ast.Str) + str_args.append(arg.s) + return str_args + + +@dataclasses.dataclass(frozen=True) +class CellMagic: + name: str + params: Optional[str] + body: str + + @property + def header(self) -> str: + if self.params: + return f"%%{self.name} {self.params}" + return f"%%{self.name}" + + +# ast.NodeVisitor + dataclass = breakage under mypyc. +class CellMagicFinder(ast.NodeVisitor): + """Find cell magics. + + Note that the source of the abstract syntax tree + will already have been processed by IPython's + TransformerManager().transform_cell. + + For example, + + %%time\nfoo() + + would have been transformed to + + get_ipython().run_cell_magic('time', '', 'foo()\\n') + + and we look for instances of the latter. + """ + + def __init__(self, cell_magic: Optional[CellMagic] = None) -> None: + self.cell_magic = cell_magic + + def visit_Expr(self, node: ast.Expr) -> None: + """Find cell magic, extract header and body.""" + if ( + isinstance(node.value, ast.Call) + and _is_ipython_magic(node.value.func) + and node.value.func.attr == "run_cell_magic" + ): + args = _get_str_args(node.value.args) + self.cell_magic = CellMagic(name=args[0], params=args[1], body=args[2]) + self.generic_visit(node) + + +@dataclasses.dataclass(frozen=True) +class OffsetAndMagic: + col_offset: int + magic: str + + +# Unsurprisingly, subclassing ast.NodeVisitor means we can't use dataclasses here +# as mypyc will generate broken code. +class MagicFinder(ast.NodeVisitor): + """Visit cell to look for get_ipython calls. + + Note that the source of the abstract syntax tree + will already have been processed by IPython's + TransformerManager().transform_cell. + + For example, + + %matplotlib inline + + would have been transformed to + + get_ipython().run_line_magic('matplotlib', 'inline') + + and we look for instances of the latter (and likewise for other + types of magics). + """ + + def __init__(self) -> None: + self.magics: Dict[int, List[OffsetAndMagic]] = collections.defaultdict(list) + + def visit_Assign(self, node: ast.Assign) -> None: + """Look for system assign magics. + + For example, + + black_version = !black --version + env = %env var + + would have been (respectively) transformed to + + black_version = get_ipython().getoutput('black --version') + env = get_ipython().run_line_magic('env', 'var') + + and we look for instances of any of the latter. + """ + if isinstance(node.value, ast.Call) and _is_ipython_magic(node.value.func): + args = _get_str_args(node.value.args) + if node.value.func.attr == "getoutput": + src = f"!{args[0]}" + elif node.value.func.attr == "run_line_magic": + src = f"%{args[0]}" + if args[1]: + src += f" {args[1]}" + else: + raise AssertionError( + f"Unexpected IPython magic {node.value.func.attr!r} found. " + "Please report a bug on https://github.com/psf/black/issues." + ) from None + self.magics[node.value.lineno].append( + OffsetAndMagic(node.value.col_offset, src) + ) + self.generic_visit(node) + + def visit_Expr(self, node: ast.Expr) -> None: + """Look for magics in body of cell. + + For examples, + + !ls + !!ls + ?ls + ??ls + + would (respectively) get transformed to + + get_ipython().system('ls') + get_ipython().getoutput('ls') + get_ipython().run_line_magic('pinfo', 'ls') + get_ipython().run_line_magic('pinfo2', 'ls') + + and we look for instances of any of the latter. + """ + if isinstance(node.value, ast.Call) and _is_ipython_magic(node.value.func): + args = _get_str_args(node.value.args) + if node.value.func.attr == "run_line_magic": + if args[0] == "pinfo": + src = f"?{args[1]}" + elif args[0] == "pinfo2": + src = f"??{args[1]}" + else: + src = f"%{args[0]}" + if args[1]: + src += f" {args[1]}" + elif node.value.func.attr == "system": + src = f"!{args[0]}" + elif node.value.func.attr == "getoutput": + src = f"!!{args[0]}" + else: + raise NothingChanged # unsupported magic. + self.magics[node.value.lineno].append( + OffsetAndMagic(node.value.col_offset, src) + ) + self.generic_visit(node) diff --git a/.venv/lib/python3.11/site-packages/black/linegen.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/linegen.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..063de20c6ab0dafdee19178dd9d7a6cda26f87cf GIT binary patch literal 8256 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 K9xD>{+W!ZQ&18`P literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/linegen.py b/.venv/lib/python3.11/site-packages/black/linegen.py new file mode 100644 index 00000000..7afb1733 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/linegen.py @@ -0,0 +1,1517 @@ +""" +Generating lines of code. +""" +import sys +from dataclasses import dataclass +from enum import Enum, auto +from functools import partial, wraps +from typing import Collection, Iterator, List, Optional, Set, Union, cast + +from black.brackets import ( + COMMA_PRIORITY, + DOT_PRIORITY, + get_leaves_inside_matching_brackets, + max_delimiter_priority_in_atom, +) +from black.comments import FMT_OFF, generate_comments, list_comments +from black.lines import ( + Line, + append_leaves, + can_be_split, + can_omit_invisible_parens, + is_line_short_enough, + line_to_string, +) +from black.mode import Feature, Mode, Preview +from black.nodes import ( + ASSIGNMENTS, + BRACKETS, + CLOSING_BRACKETS, + OPENING_BRACKETS, + RARROW, + STANDALONE_COMMENT, + STATEMENT, + WHITESPACE, + Visitor, + ensure_visible, + is_arith_like, + is_atom_with_invisible_parens, + is_docstring, + is_empty_tuple, + is_lpar_token, + is_multiline_string, + is_name_token, + is_one_sequence_between, + is_one_tuple, + is_rpar_token, + is_stub_body, + is_stub_suite, + is_tuple_containing_walrus, + is_vararg, + is_walrus_assignment, + is_yield, + syms, + wrap_in_parentheses, +) +from black.numerics import normalize_numeric_literal +from black.strings import ( + fix_docstring, + get_string_prefix, + normalize_string_prefix, + normalize_string_quotes, + normalize_unicode_escape_sequences, +) +from black.trans import ( + CannotTransform, + StringMerger, + StringParenStripper, + StringParenWrapper, + StringSplitter, + Transformer, + hug_power_op, +) +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + +# types +LeafID = int +LN = Union[Leaf, Node] + + +class CannotSplit(CannotTransform): + """A readable split that fits the allotted line length is impossible.""" + + +# This isn't a dataclass because @dataclass + Generic breaks mypyc. +# See also https://github.com/mypyc/mypyc/issues/827. +class LineGenerator(Visitor[Line]): + """Generates reformatted Line objects. Empty lines are not emitted. + + Note: destroys the tree it's visiting by mutating prefixes of its leaves + in ways that will no longer stringify to valid Python code on the tree. + """ + + def __init__(self, mode: Mode, features: Collection[Feature]) -> None: + self.mode = mode + self.features = features + self.current_line: Line + self.__post_init__() + + def line(self, indent: int = 0) -> Iterator[Line]: + """Generate a line. + + If the line is empty, only emit if it makes sense. + If the line is too long, split it first and then generate. + + If any lines were generated, set up a new current_line. + """ + if not self.current_line: + self.current_line.depth += indent + return # Line is empty, don't emit. Creating a new one unnecessary. + + complete_line = self.current_line + self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent) + yield complete_line + + def visit_default(self, node: LN) -> Iterator[Line]: + """Default `visit_*()` implementation. Recurses to children of `node`.""" + if isinstance(node, Leaf): + any_open_brackets = self.current_line.bracket_tracker.any_open_brackets() + for comment in generate_comments(node): + if any_open_brackets: + # any comment within brackets is subject to splitting + self.current_line.append(comment) + elif comment.type == token.COMMENT: + # regular trailing comment + self.current_line.append(comment) + yield from self.line() + + else: + # regular standalone comment + yield from self.line() + + self.current_line.append(comment) + yield from self.line() + + normalize_prefix(node, inside_brackets=any_open_brackets) + if self.mode.string_normalization and node.type == token.STRING: + node.value = normalize_string_prefix(node.value) + node.value = normalize_string_quotes(node.value) + if node.type == token.NUMBER: + normalize_numeric_literal(node) + if node.type not in WHITESPACE: + self.current_line.append(node) + yield from super().visit_default(node) + + def visit_test(self, node: Node) -> Iterator[Line]: + """Visit an `x if y else z` test""" + + if Preview.parenthesize_conditional_expressions in self.mode: + already_parenthesized = ( + node.prev_sibling and node.prev_sibling.type == token.LPAR + ) + + if not already_parenthesized: + lpar = Leaf(token.LPAR, "") + rpar = Leaf(token.RPAR, "") + node.insert_child(0, lpar) + node.append_child(rpar) + + yield from self.visit_default(node) + + def visit_INDENT(self, node: Leaf) -> Iterator[Line]: + """Increase indentation level, maybe yield a line.""" + # In blib2to3 INDENT never holds comments. + yield from self.line(+1) + yield from self.visit_default(node) + + def visit_DEDENT(self, node: Leaf) -> Iterator[Line]: + """Decrease indentation level, maybe yield a line.""" + # The current line might still wait for trailing comments. At DEDENT time + # there won't be any (they would be prefixes on the preceding NEWLINE). + # Emit the line then. + yield from self.line() + + # While DEDENT has no value, its prefix may contain standalone comments + # that belong to the current indentation level. Get 'em. + yield from self.visit_default(node) + + # Finally, emit the dedent. + yield from self.line(-1) + + def visit_stmt( + self, node: Node, keywords: Set[str], parens: Set[str] + ) -> Iterator[Line]: + """Visit a statement. + + This implementation is shared for `if`, `while`, `for`, `try`, `except`, + `def`, `with`, `class`, `assert`, and assignments. + + The relevant Python language `keywords` for a given statement will be + NAME leaves within it. This methods puts those on a separate line. + + `parens` holds a set of string leaf values immediately after which + invisible parens should be put. + """ + normalize_invisible_parens( + node, parens_after=parens, mode=self.mode, features=self.features + ) + for child in node.children: + if is_name_token(child) and child.value in keywords: + yield from self.line() + + yield from self.visit(child) + + def visit_dictsetmaker(self, node: Node) -> Iterator[Line]: + if Preview.wrap_long_dict_values_in_parens in self.mode: + for i, child in enumerate(node.children): + if i == 0: + continue + if node.children[i - 1].type == token.COLON: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=False, + ): + wrap_in_parentheses(node, child, visible=False) + else: + wrap_in_parentheses(node, child, visible=False) + yield from self.visit_default(node) + + def visit_funcdef(self, node: Node) -> Iterator[Line]: + """Visit function definition.""" + yield from self.line() + + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=False, + ): + wrap_in_parentheses(node, child, visible=False) + else: + wrap_in_parentheses(node, child, visible=False) + is_return_annotation = False + + for child in node.children: + yield from self.visit(child) + + def visit_match_case(self, node: Node) -> Iterator[Line]: + """Visit either a match or case statement.""" + normalize_invisible_parens( + node, parens_after=set(), mode=self.mode, features=self.features + ) + + yield from self.line() + for child in node.children: + yield from self.visit(child) + + def visit_suite(self, node: Node) -> Iterator[Line]: + """Visit a suite.""" + if self.mode.is_pyi and is_stub_suite(node): + yield from self.visit(node.children[2]) + else: + yield from self.visit_default(node) + + def visit_simple_stmt(self, node: Node) -> Iterator[Line]: + """Visit a statement without nested statements.""" + prev_type: Optional[int] = None + for child in node.children: + if (prev_type is None or prev_type == token.SEMI) and is_arith_like(child): + wrap_in_parentheses(node, child, visible=False) + prev_type = child.type + + is_suite_like = node.parent and node.parent.type in STATEMENT + if is_suite_like: + if self.mode.is_pyi and is_stub_body(node): + yield from self.visit_default(node) + else: + yield from self.line(+1) + yield from self.visit_default(node) + yield from self.line(-1) + + else: + if ( + not self.mode.is_pyi + or not node.parent + or not is_stub_suite(node.parent) + ): + yield from self.line() + yield from self.visit_default(node) + + def visit_async_stmt(self, node: Node) -> Iterator[Line]: + """Visit `async def`, `async for`, `async with`.""" + yield from self.line() + + children = iter(node.children) + for child in children: + yield from self.visit(child) + + if child.type == token.ASYNC or child.type == STANDALONE_COMMENT: + # STANDALONE_COMMENT happens when `# fmt: skip` is applied on the async + # line. + break + + internal_stmt = next(children) + for child in internal_stmt.children: + yield from self.visit(child) + + def visit_decorators(self, node: Node) -> Iterator[Line]: + """Visit decorators.""" + for child in node.children: + yield from self.line() + yield from self.visit(child) + + def visit_power(self, node: Node) -> Iterator[Line]: + for idx, leaf in enumerate(node.children[:-1]): + next_leaf = node.children[idx + 1] + + if not isinstance(leaf, Leaf): + continue + + value = leaf.value.lower() + if ( + leaf.type == token.NUMBER + and next_leaf.type == syms.trailer + # Ensure that we are in an attribute trailer + and next_leaf.children[0].type == token.DOT + # It shouldn't wrap hexadecimal, binary and octal literals + and not value.startswith(("0x", "0b", "0o")) + # It shouldn't wrap complex literals + and "j" not in value + ): + wrap_in_parentheses(node, leaf) + + remove_await_parens(node) + + yield from self.visit_default(node) + + def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]: + """Remove a semicolon and put the other statement on a separate line.""" + yield from self.line() + + def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]: + """End of file. Process outstanding comments and end with a newline.""" + yield from self.visit_default(leaf) + yield from self.line() + + def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]: + if not self.current_line.bracket_tracker.any_open_brackets(): + yield from self.line() + yield from self.visit_default(leaf) + + def visit_factor(self, node: Node) -> Iterator[Line]: + """Force parentheses between a unary op and a binary power: + + -2 ** 8 -> -(2 ** 8) + """ + _operator, operand = node.children + if ( + operand.type == syms.power + and len(operand.children) == 3 + and operand.children[1].type == token.DOUBLESTAR + ): + lpar = Leaf(token.LPAR, "(") + rpar = Leaf(token.RPAR, ")") + index = operand.remove() or 0 + node.insert_child(index, Node(syms.atom, [lpar, operand, rpar])) + yield from self.visit_default(node) + + def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: + if Preview.hex_codes_in_unicode_sequences in self.mode: + normalize_unicode_escape_sequences(leaf) + + if is_docstring(leaf) and "\\\n" not in leaf.value: + # We're ignoring docstrings with backslash newline escapes because changing + # indentation of those changes the AST representation of the code. + if self.mode.string_normalization: + docstring = normalize_string_prefix(leaf.value) + # visit_default() does handle string normalization for us, but + # since this method acts differently depending on quote style (ex. + # see padding logic below), there's a possibility for unstable + # formatting as visit_default() is called *after*. To avoid a + # situation where this function formats a docstring differently on + # the second pass, normalize it early. + docstring = normalize_string_quotes(docstring) + else: + docstring = leaf.value + prefix = get_string_prefix(docstring) + docstring = docstring[len(prefix) :] # Remove the prefix + quote_char = docstring[0] + # A natural way to remove the outer quotes is to do: + # docstring = docstring.strip(quote_char) + # but that breaks on """""x""" (which is '""x'). + # So we actually need to remove the first character and the next two + # characters but only if they are the same as the first. + quote_len = 1 if docstring[1] != quote_char else 3 + docstring = docstring[quote_len:-quote_len] + docstring_started_empty = not docstring + indent = " " * 4 * self.current_line.depth + + if is_multiline_string(leaf): + docstring = fix_docstring(docstring, indent) + else: + docstring = docstring.strip() + + has_trailing_backslash = False + if docstring: + # Add some padding if the docstring starts / ends with a quote mark. + if docstring[0] == quote_char: + docstring = " " + docstring + if docstring[-1] == quote_char: + docstring += " " + if docstring[-1] == "\\": + backslash_count = len(docstring) - len(docstring.rstrip("\\")) + if backslash_count % 2: + # Odd number of tailing backslashes, add some padding to + # avoid escaping the closing string quote. + docstring += " " + has_trailing_backslash = True + elif not docstring_started_empty: + docstring = " " + + # We could enforce triple quotes at this point. + quote = quote_char * quote_len + + # It's invalid to put closing single-character quotes on a new line. + if self.mode and quote_len == 3: + # We need to find the length of the last line of the docstring + # to find if we can add the closing quotes to the line without + # exceeding the maximum line length. + # If docstring is one line, we don't put the closing quotes on a + # separate line because it looks ugly (#3320). + lines = docstring.splitlines() + last_line_length = len(lines[-1]) if docstring else 0 + + # If adding closing quotes would cause the last line to exceed + # the maximum line length then put a line break before the + # closing quotes + if ( + len(lines) > 1 + and last_line_length + quote_len > self.mode.line_length + and len(indent) + quote_len <= self.mode.line_length + and not has_trailing_backslash + ): + leaf.value = prefix + quote + docstring + "\n" + indent + quote + else: + leaf.value = prefix + quote + docstring + quote + else: + leaf.value = prefix + quote + docstring + quote + + yield from self.visit_default(leaf) + + def __post_init__(self) -> None: + """You are in a twisty little maze of passages.""" + self.current_line = Line(mode=self.mode) + + v = self.visit_stmt + Ø: Set[str] = set() + self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","}) + self.visit_if_stmt = partial( + v, keywords={"if", "else", "elif"}, parens={"if", "elif"} + ) + self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"}) + self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"}) + self.visit_try_stmt = partial( + v, keywords={"try", "except", "else", "finally"}, parens=Ø + ) + self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"}) + self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"}) + self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) + self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS) + self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"}) + self.visit_import_from = partial(v, keywords=Ø, parens={"import"}) + self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"}) + self.visit_async_funcdef = self.visit_async_stmt + self.visit_decorated = self.visit_decorators + + # PEP 634 + self.visit_match_stmt = self.visit_match_case + self.visit_case_block = self.visit_match_case + + +def transform_line( + line: Line, mode: Mode, features: Collection[Feature] = () +) -> Iterator[Line]: + """Transform a `line`, potentially splitting it into many lines. + + They should fit in the allotted `line_length` but might not be able to. + + `features` are syntactical features that may be used in the output. + """ + if line.is_comment: + yield line + return + + line_str = line_to_string(line) + + ll = mode.line_length + sn = mode.string_normalization + string_merge = StringMerger(ll, sn) + string_paren_strip = StringParenStripper(ll, sn) + string_split = StringSplitter(ll, sn) + string_paren_wrap = StringParenWrapper(ll, sn) + + transformers: List[Transformer] + if ( + not line.contains_uncollapsable_type_comments() + and not line.should_split_rhs + and not line.magic_trailing_comma + and ( + is_line_short_enough(line, line_length=mode.line_length, line_str=line_str) + or line.contains_unsplittable_type_ignore() + ) + and not (line.inside_brackets and line.contains_standalone_comments()) + ): + # Only apply basic string preprocessing, since lines shouldn't be split here. + if Preview.string_processing in mode: + transformers = [string_merge, string_paren_strip] + else: + transformers = [] + elif line.is_def: + transformers = [left_hand_split] + else: + + def _rhs( + self: object, line: Line, features: Collection[Feature] + ) -> Iterator[Line]: + """Wraps calls to `right_hand_split`. + + The calls increasingly `omit` right-hand trailers (bracket pairs with + content), meaning the trailers get glued together to split on another + bracket pair instead. + """ + for omit in generate_trailers_to_omit(line, mode.line_length): + lines = list( + right_hand_split(line, mode.line_length, features, omit=omit) + ) + # Note: this check is only able to figure out if the first line of the + # *current* transformation fits in the line length. This is true only + # for simple cases. All others require running more transforms via + # `transform_line()`. This check doesn't know if those would succeed. + if is_line_short_enough(lines[0], line_length=mode.line_length): + yield from lines + return + + # All splits failed, best effort split with no omits. + # This mostly happens to multiline strings that are by definition + # reported as not fitting a single line, as well as lines that contain + # trailing commas (those have to be exploded). + yield from right_hand_split( + line, line_length=mode.line_length, features=features + ) + + # HACK: nested functions (like _rhs) compiled by mypyc don't retain their + # __name__ attribute which is needed in `run_transformer` further down. + # Unfortunately a nested class breaks mypyc too. So a class must be created + # via type ... https://github.com/mypyc/mypyc/issues/884 + rhs = type("rhs", (), {"__call__": _rhs})() + + if Preview.string_processing in mode: + if line.inside_brackets: + transformers = [ + string_merge, + string_paren_strip, + string_split, + delimiter_split, + standalone_comment_split, + string_paren_wrap, + rhs, + ] + else: + transformers = [ + string_merge, + string_paren_strip, + string_split, + string_paren_wrap, + rhs, + ] + else: + if line.inside_brackets: + transformers = [delimiter_split, standalone_comment_split, rhs] + else: + transformers = [rhs] + # It's always safe to attempt hugging of power operations and pretty much every line + # could match. + transformers.append(hug_power_op) + + for transform in transformers: + # We are accumulating lines in `result` because we might want to abort + # mission and return the original line in the end, or attempt a different + # split altogether. + try: + result = run_transformer(line, transform, mode, features, line_str=line_str) + except CannotTransform: + continue + else: + yield from result + break + + else: + yield line + + +class _BracketSplitComponent(Enum): + head = auto() + body = auto() + tail = auto() + + +def left_hand_split(line: Line, _features: Collection[Feature] = ()) -> Iterator[Line]: + """Split line into many lines, starting with the first matching bracket pair. + + Note: this usually looks weird, only use this for function definitions. + Prefer RHS otherwise. This is why this function is not symmetrical with + :func:`right_hand_split` which also handles optional parentheses. + """ + tail_leaves: List[Leaf] = [] + body_leaves: List[Leaf] = [] + head_leaves: List[Leaf] = [] + current_leaves = head_leaves + matching_bracket: Optional[Leaf] = None + for leaf in line.leaves: + if ( + current_leaves is body_leaves + and leaf.type in CLOSING_BRACKETS + and leaf.opening_bracket is matching_bracket + and isinstance(matching_bracket, Leaf) + ): + ensure_visible(leaf) + ensure_visible(matching_bracket) + current_leaves = tail_leaves if body_leaves else head_leaves + current_leaves.append(leaf) + if current_leaves is head_leaves: + if leaf.type in OPENING_BRACKETS: + matching_bracket = leaf + current_leaves = body_leaves + if not matching_bracket: + raise CannotSplit("No brackets found") + + head = bracket_split_build_line( + head_leaves, line, matching_bracket, component=_BracketSplitComponent.head + ) + body = bracket_split_build_line( + body_leaves, line, matching_bracket, component=_BracketSplitComponent.body + ) + tail = bracket_split_build_line( + tail_leaves, line, matching_bracket, component=_BracketSplitComponent.tail + ) + bracket_split_succeeded_or_raise(head, body, tail) + for result in (head, body, tail): + if result: + yield result + + +@dataclass +class _RHSResult: + """Intermediate split result from a right hand split.""" + + head: Line + body: Line + tail: Line + opening_bracket: Leaf + closing_bracket: Leaf + + +def right_hand_split( + line: Line, + line_length: int, + features: Collection[Feature] = (), + omit: Collection[LeafID] = (), +) -> Iterator[Line]: + """Split line into many lines, starting with the last matching bracket pair. + + If the split was by optional parentheses, attempt splitting without them, too. + `omit` is a collection of closing bracket IDs that shouldn't be considered for + this split. + + Note: running this function modifies `bracket_depth` on the leaves of `line`. + """ + rhs_result = _first_right_hand_split(line, omit=omit) + yield from _maybe_split_omitting_optional_parens( + rhs_result, line, line_length, features=features, omit=omit + ) + + +def _first_right_hand_split( + line: Line, + omit: Collection[LeafID] = (), +) -> _RHSResult: + """Split the line into head, body, tail starting with the last bracket pair. + + Note: this function should not have side effects. It's relied upon by + _maybe_split_omitting_optional_parens to get an opinion whether to prefer + splitting on the right side of an assignment statement. + """ + tail_leaves: List[Leaf] = [] + body_leaves: List[Leaf] = [] + head_leaves: List[Leaf] = [] + current_leaves = tail_leaves + opening_bracket: Optional[Leaf] = None + closing_bracket: Optional[Leaf] = None + for leaf in reversed(line.leaves): + if current_leaves is body_leaves: + if leaf is opening_bracket: + current_leaves = head_leaves if body_leaves else tail_leaves + current_leaves.append(leaf) + if current_leaves is tail_leaves: + if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit: + opening_bracket = leaf.opening_bracket + closing_bracket = leaf + current_leaves = body_leaves + if not (opening_bracket and closing_bracket and head_leaves): + # If there is no opening or closing_bracket that means the split failed and + # all content is in the tail. Otherwise, if `head_leaves` are empty, it means + # the matching `opening_bracket` wasn't available on `line` anymore. + raise CannotSplit("No brackets found") + + tail_leaves.reverse() + body_leaves.reverse() + head_leaves.reverse() + head = bracket_split_build_line( + head_leaves, line, opening_bracket, component=_BracketSplitComponent.head + ) + body = bracket_split_build_line( + body_leaves, line, opening_bracket, component=_BracketSplitComponent.body + ) + tail = bracket_split_build_line( + tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail + ) + bracket_split_succeeded_or_raise(head, body, tail) + return _RHSResult(head, body, tail, opening_bracket, closing_bracket) + + +def _maybe_split_omitting_optional_parens( + rhs: _RHSResult, + line: Line, + line_length: int, + features: Collection[Feature] = (), + omit: Collection[LeafID] = (), +) -> Iterator[Line]: + if ( + Feature.FORCE_OPTIONAL_PARENTHESES not in features + # the opening bracket is an optional paren + and rhs.opening_bracket.type == token.LPAR + and not rhs.opening_bracket.value + # the closing bracket is an optional paren + and rhs.closing_bracket.type == token.RPAR + and not rhs.closing_bracket.value + # it's not an import (optional parens are the only thing we can split on + # in this case; attempting a split without them is a waste of time) + and not line.is_import + # there are no standalone comments in the body + and not rhs.body.contains_standalone_comments(0) + # and we can actually remove the parens + and can_omit_invisible_parens(rhs.body, line_length) + ): + omit = {id(rhs.closing_bracket), *omit} + try: + # The _RHSResult Omitting Optional Parens. + rhs_oop = _first_right_hand_split(line, omit=omit) + if not ( + Preview.prefer_splitting_right_hand_side_of_assignments in line.mode + # the split is right after `=` + and len(rhs.head.leaves) >= 2 + and rhs.head.leaves[-2].type == token.EQUAL + # the left side of assignement contains brackets + and any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1]) + # the left side of assignment is short enough (the -1 is for the ending + # optional paren) + and is_line_short_enough(rhs.head, line_length=line_length - 1) + # the left side of assignment won't explode further because of magic + # trailing comma + and rhs.head.magic_trailing_comma is None + # the split by omitting optional parens isn't preferred by some other + # reason + and not _prefer_split_rhs_oop(rhs_oop, line_length=line_length) + ): + yield from _maybe_split_omitting_optional_parens( + rhs_oop, line, line_length, features=features, omit=omit + ) + return + + except CannotSplit as e: + if not ( + can_be_split(rhs.body) + or is_line_short_enough(rhs.body, line_length=line_length) + ): + raise CannotSplit( + "Splitting failed, body is still too long and can't be split." + ) from e + + elif ( + rhs.head.contains_multiline_strings() + or rhs.tail.contains_multiline_strings() + ): + raise CannotSplit( + "The current optional pair of parentheses is bound to fail to" + " satisfy the splitting algorithm because the head or the tail" + " contains multiline strings which by definition never fit one" + " line." + ) from e + + ensure_visible(rhs.opening_bracket) + ensure_visible(rhs.closing_bracket) + for result in (rhs.head, rhs.body, rhs.tail): + if result: + yield result + + +def _prefer_split_rhs_oop(rhs_oop: _RHSResult, line_length: int) -> bool: + """ + Returns whether we should prefer the result from a split omitting optional parens. + """ + has_closing_bracket_after_assign = False + for leaf in reversed(rhs_oop.head.leaves): + if leaf.type == token.EQUAL: + break + if leaf.type in CLOSING_BRACKETS: + has_closing_bracket_after_assign = True + break + return ( + # contains matching brackets after the `=` (done by checking there is a + # closing bracket) + has_closing_bracket_after_assign + or ( + # the split is actually from inside the optional parens (done by checking + # the first line still contains the `=`) + any(leaf.type == token.EQUAL for leaf in rhs_oop.head.leaves) + # the first line is short enough + and is_line_short_enough(rhs_oop.head, line_length=line_length) + ) + # contains unsplittable type ignore + or rhs_oop.head.contains_unsplittable_type_ignore() + or rhs_oop.body.contains_unsplittable_type_ignore() + or rhs_oop.tail.contains_unsplittable_type_ignore() + ) + + +def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None: + """Raise :exc:`CannotSplit` if the last left- or right-hand split failed. + + Do nothing otherwise. + + A left- or right-hand split is based on a pair of brackets. Content before + (and including) the opening bracket is left on one line, content inside the + brackets is put on a separate line, and finally content starting with and + following the closing bracket is put on a separate line. + + Those are called `head`, `body`, and `tail`, respectively. If the split + produced the same line (all content in `head`) or ended up with an empty `body` + and the `tail` is just the closing bracket, then it's considered failed. + """ + tail_len = len(str(tail).strip()) + if not body: + if tail_len == 0: + raise CannotSplit("Splitting brackets produced the same line") + + elif tail_len < 3: + raise CannotSplit( + f"Splitting brackets on an empty body to save {tail_len} characters is" + " not worth it" + ) + + +def bracket_split_build_line( + leaves: List[Leaf], + original: Line, + opening_bracket: Leaf, + *, + component: _BracketSplitComponent, +) -> Line: + """Return a new line with given `leaves` and respective comments from `original`. + + If it's the head component, brackets will be tracked so trailing commas are + respected. + + If it's the body component, the result line is one-indented inside brackets and as + such has its first leaf's prefix normalized and a trailing comma added when + expected. + """ + result = Line(mode=original.mode, depth=original.depth) + if component is _BracketSplitComponent.body: + result.inside_brackets = True + result.depth += 1 + if leaves: + # Since body is a new indent level, remove spurious leading whitespace. + normalize_prefix(leaves[0], inside_brackets=True) + # Ensure a trailing comma for imports and standalone function arguments, but + # be careful not to add one after any comments or within type annotations. + no_commas = ( + original.is_def + and opening_bracket.value == "(" + and not any(leaf.type == token.COMMA for leaf in leaves) + # In particular, don't add one within a parenthesized return annotation. + # Unfortunately the indicator we're in a return annotation (RARROW) may + # be defined directly in the parent node, the parent of the parent ... + # and so on depending on how complex the return annotation is. + # This isn't perfect and there's some false negatives but they are in + # contexts were a comma is actually fine. + and not any( + node.prev_sibling.type == RARROW + for node in ( + leaves[0].parent, + getattr(leaves[0].parent, "parent", None), + ) + if isinstance(node, Node) and isinstance(node.prev_sibling, Leaf) + ) + ) + + if original.is_import or no_commas: + for i in range(len(leaves) - 1, -1, -1): + if leaves[i].type == STANDALONE_COMMENT: + continue + + if leaves[i].type != token.COMMA: + new_comma = Leaf(token.COMMA, ",") + leaves.insert(i + 1, new_comma) + break + + leaves_to_track: Set[LeafID] = set() + if component is _BracketSplitComponent.head: + leaves_to_track = get_leaves_inside_matching_brackets(leaves) + # Populate the line + for leaf in leaves: + result.append( + leaf, + preformatted=True, + track_bracket=id(leaf) in leaves_to_track, + ) + for comment_after in original.comments_after(leaf): + result.append(comment_after, preformatted=True) + if component is _BracketSplitComponent.body and should_split_line( + result, opening_bracket + ): + result.should_split_rhs = True + return result + + +def dont_increase_indentation(split_func: Transformer) -> Transformer: + """Normalize prefix of the first leaf in every line returned by `split_func`. + + This is a decorator over relevant split functions. + """ + + @wraps(split_func) + def split_wrapper(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]: + for split_line in split_func(line, features): + normalize_prefix(split_line.leaves[0], inside_brackets=True) + yield split_line + + return split_wrapper + + +@dont_increase_indentation +def delimiter_split(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]: + """Split according to delimiters of the highest priority. + + If the appropriate Features are given, the split will add trailing commas + also in function signatures and calls that contain `*` and `**`. + """ + try: + last_leaf = line.leaves[-1] + except IndexError: + raise CannotSplit("Line empty") from None + + bt = line.bracket_tracker + try: + delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)}) + except ValueError: + raise CannotSplit("No delimiters found") from None + + if delimiter_priority == DOT_PRIORITY: + if bt.delimiter_count_with_priority(delimiter_priority) == 1: + raise CannotSplit("Splitting a single attribute from its owner looks wrong") + + current_line = Line( + mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets + ) + lowest_depth = sys.maxsize + trailing_comma_safe = True + + def append_to_line(leaf: Leaf) -> Iterator[Line]: + """Append `leaf` to current line or to new line if appending impossible.""" + nonlocal current_line + try: + current_line.append_safe(leaf, preformatted=True) + except ValueError: + yield current_line + + current_line = Line( + mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets + ) + current_line.append(leaf) + + for leaf in line.leaves: + yield from append_to_line(leaf) + + for comment_after in line.comments_after(leaf): + yield from append_to_line(comment_after) + + lowest_depth = min(lowest_depth, leaf.bracket_depth) + if leaf.bracket_depth == lowest_depth: + if is_vararg(leaf, within={syms.typedargslist}): + trailing_comma_safe = ( + trailing_comma_safe and Feature.TRAILING_COMMA_IN_DEF in features + ) + elif is_vararg(leaf, within={syms.arglist, syms.argument}): + trailing_comma_safe = ( + trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features + ) + + leaf_priority = bt.delimiters.get(id(leaf)) + if leaf_priority == delimiter_priority: + yield current_line + + current_line = Line( + mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets + ) + if current_line: + if ( + trailing_comma_safe + and delimiter_priority == COMMA_PRIORITY + and current_line.leaves[-1].type != token.COMMA + and current_line.leaves[-1].type != STANDALONE_COMMENT + ): + new_comma = Leaf(token.COMMA, ",") + current_line.append(new_comma) + yield current_line + + +@dont_increase_indentation +def standalone_comment_split( + line: Line, features: Collection[Feature] = () +) -> Iterator[Line]: + """Split standalone comments from the rest of the line.""" + if not line.contains_standalone_comments(0): + raise CannotSplit("Line does not have any standalone comments") + + current_line = Line( + mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets + ) + + def append_to_line(leaf: Leaf) -> Iterator[Line]: + """Append `leaf` to current line or to new line if appending impossible.""" + nonlocal current_line + try: + current_line.append_safe(leaf, preformatted=True) + except ValueError: + yield current_line + + current_line = Line( + line.mode, depth=line.depth, inside_brackets=line.inside_brackets + ) + current_line.append(leaf) + + for leaf in line.leaves: + yield from append_to_line(leaf) + + for comment_after in line.comments_after(leaf): + yield from append_to_line(comment_after) + + if current_line: + yield current_line + + +def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None: + """Leave existing extra newlines if not `inside_brackets`. Remove everything + else. + + Note: don't use backslashes for formatting or you'll lose your voting rights. + """ + if not inside_brackets: + spl = leaf.prefix.split("#") + if "\\" not in spl[0]: + nl_count = spl[-1].count("\n") + if len(spl) > 1: + nl_count -= 1 + leaf.prefix = "\n" * nl_count + return + + leaf.prefix = "" + + +def normalize_invisible_parens( + node: Node, parens_after: Set[str], *, mode: Mode, features: Collection[Feature] +) -> None: + """Make existing optional parentheses invisible or create new ones. + + `parens_after` is a set of string leaf values immediately after which parens + should be put. + + Standardizes on visible parentheses for single-element tuples, and keeps + existing visible parentheses for other tuples and generator expressions. + """ + for pc in list_comments(node.prefix, is_endmarker=False): + if pc.value in FMT_OFF: + # This `node` has a prefix with `# fmt: off`, don't mess with parens. + return + + # The multiple context managers grammar has a different pattern, thus this is + # separate from the for-loop below. This possibly wraps them in invisible parens, + # and later will be removed in remove_with_parens when needed. + if node.type == syms.with_stmt: + _maybe_wrap_cms_in_parens(node, mode, features) + + check_lpar = False + for index, child in enumerate(list(node.children)): + # Fixes a bug where invisible parens are not properly stripped from + # assignment statements that contain type annotations. + if isinstance(child, Node) and child.type == syms.annassign: + normalize_invisible_parens( + child, parens_after=parens_after, mode=mode, features=features + ) + + # Add parentheses around long tuple unpacking in assignments. + if ( + index == 0 + and isinstance(child, Node) + and child.type == syms.testlist_star_expr + ): + check_lpar = True + + if check_lpar: + if ( + child.type == syms.atom + and node.type == syms.for_stmt + and isinstance(child.prev_sibling, Leaf) + and child.prev_sibling.type == token.NAME + and child.prev_sibling.value == "for" + ): + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=True, + ): + wrap_in_parentheses(node, child, visible=False) + elif isinstance(child, Node) and node.type == syms.with_stmt: + remove_with_parens(child, node) + elif child.type == syms.atom: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + ): + wrap_in_parentheses(node, child, visible=False) + elif is_one_tuple(child): + wrap_in_parentheses(node, child, visible=True) + elif node.type == syms.import_from: + _normalize_import_from(node, child, index) + break + elif ( + index == 1 + and child.type == token.STAR + and node.type == syms.except_clause + ): + # In except* (PEP 654), the star is actually part of + # of the keyword. So we need to skip the insertion of + # invisible parentheses to work more precisely. + continue + + elif not (isinstance(child, Leaf) and is_multiline_string(child)): + wrap_in_parentheses(node, child, visible=False) + + comma_check = child.type == token.COMMA + + check_lpar = isinstance(child, Leaf) and ( + child.value in parens_after or comma_check + ) + + +def _normalize_import_from(parent: Node, child: LN, index: int) -> None: + # "import from" nodes store parentheses directly as part of + # the statement + if is_lpar_token(child): + assert is_rpar_token(parent.children[-1]) + # make parentheses invisible + child.value = "" + parent.children[-1].value = "" + elif child.type != token.STAR: + # insert invisible parentheses + parent.insert_child(index, Leaf(token.LPAR, "")) + parent.append_child(Leaf(token.RPAR, "")) + + +def remove_await_parens(node: Node) -> None: + if node.children[0].type == token.AWAIT and len(node.children) > 1: + if ( + node.children[1].type == syms.atom + and node.children[1].children[0].type == token.LPAR + ): + if maybe_make_parens_invisible_in_atom( + node.children[1], + parent=node, + remove_brackets_around_comma=True, + ): + wrap_in_parentheses(node, node.children[1], visible=False) + + # Since await is an expression we shouldn't remove + # brackets in cases where this would change + # the AST due to operator precedence. + # Therefore we only aim to remove brackets around + # power nodes that aren't also await expressions themselves. + # https://peps.python.org/pep-0492/#updated-operator-precedence-table + # N.B. We've still removed any redundant nested brackets though :) + opening_bracket = cast(Leaf, node.children[1].children[0]) + closing_bracket = cast(Leaf, node.children[1].children[-1]) + bracket_contents = node.children[1].children[1] + if isinstance(bracket_contents, Node): + if bracket_contents.type != syms.power: + ensure_visible(opening_bracket) + ensure_visible(closing_bracket) + elif ( + bracket_contents.type == syms.power + and bracket_contents.children[0].type == token.AWAIT + ): + ensure_visible(opening_bracket) + ensure_visible(closing_bracket) + # If we are in a nested await then recurse down. + remove_await_parens(bracket_contents) + + +def _maybe_wrap_cms_in_parens( + node: Node, mode: Mode, features: Collection[Feature] +) -> None: + """When enabled and safe, wrap the multiple context managers in invisible parens. + + It is only safe when `features` contain Feature.PARENTHESIZED_CONTEXT_MANAGERS. + """ + if ( + Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features + or Preview.wrap_multiple_context_managers_in_parens not in mode + or len(node.children) <= 2 + # If it's an atom, it's already wrapped in parens. + or node.children[1].type == syms.atom + ): + return + colon_index: Optional[int] = None + for i in range(2, len(node.children)): + if node.children[i].type == token.COLON: + colon_index = i + break + if colon_index is not None: + lpar = Leaf(token.LPAR, "") + rpar = Leaf(token.RPAR, "") + context_managers = node.children[1:colon_index] + for child in context_managers: + child.remove() + # After wrapping, the with_stmt will look like this: + # with_stmt + # NAME 'with' + # atom + # LPAR '' + # testlist_gexp + # ... <-- context_managers + # /testlist_gexp + # RPAR '' + # /atom + # COLON ':' + new_child = Node( + syms.atom, [lpar, Node(syms.testlist_gexp, context_managers), rpar] + ) + node.insert_child(1, new_child) + + +def remove_with_parens(node: Node, parent: Node) -> None: + """Recursively hide optional parens in `with` statements.""" + # Removing all unnecessary parentheses in with statements in one pass is a tad + # complex as different variations of bracketed statements result in pretty + # different parse trees: + # + # with (open("file")) as f: # this is an asexpr_test + # ... + # + # with (open("file") as f): # this is an atom containing an + # ... # asexpr_test + # + # with (open("file")) as f, (open("file")) as f: # this is asexpr_test, COMMA, + # ... # asexpr_test + # + # with (open("file") as f, open("file") as f): # an atom containing a + # ... # testlist_gexp which then + # # contains multiple asexpr_test(s) + if node.type == syms.atom: + if maybe_make_parens_invisible_in_atom( + node, + parent=parent, + remove_brackets_around_comma=True, + ): + wrap_in_parentheses(parent, node, visible=False) + if isinstance(node.children[1], Node): + remove_with_parens(node.children[1], node) + elif node.type == syms.testlist_gexp: + for child in node.children: + if isinstance(child, Node): + remove_with_parens(child, node) + elif node.type == syms.asexpr_test and not any( + leaf.type == token.COLONEQUAL for leaf in node.leaves() + ): + if maybe_make_parens_invisible_in_atom( + node.children[0], + parent=node, + remove_brackets_around_comma=True, + ): + wrap_in_parentheses(node, node.children[0], visible=False) + + +def maybe_make_parens_invisible_in_atom( + node: LN, + parent: LN, + remove_brackets_around_comma: bool = False, +) -> bool: + """If it's safe, make the parens in the atom `node` invisible, recursively. + Additionally, remove repeated, adjacent invisible parens from the atom `node` + as they are redundant. + + Returns whether the node should itself be wrapped in invisible parentheses. + """ + if ( + node.type != syms.atom + or is_empty_tuple(node) + or is_one_tuple(node) + or (is_yield(node) and parent.type != syms.expr_stmt) + or ( + # This condition tries to prevent removing non-optional brackets + # around a tuple, however, can be a bit overzealous so we provide + # and option to skip this check for `for` and `with` statements. + not remove_brackets_around_comma + and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY + ) + or is_tuple_containing_walrus(node) + ): + return False + + if is_walrus_assignment(node): + if parent.type in [ + syms.annassign, + syms.expr_stmt, + syms.assert_stmt, + syms.return_stmt, + syms.except_clause, + syms.funcdef, + syms.with_stmt, + # these ones aren't useful to end users, but they do please fuzzers + syms.for_stmt, + syms.del_stmt, + syms.for_stmt, + ]: + return False + + first = node.children[0] + last = node.children[-1] + if is_lpar_token(first) and is_rpar_token(last): + middle = node.children[1] + # make parentheses invisible + first.value = "" + last.value = "" + maybe_make_parens_invisible_in_atom( + middle, + parent=parent, + remove_brackets_around_comma=remove_brackets_around_comma, + ) + + if is_atom_with_invisible_parens(middle): + # Strip the invisible parens from `middle` by replacing + # it with the child in-between the invisible parens + middle.replace(middle.children[1]) + + return False + + return True + + +def should_split_line(line: Line, opening_bracket: Leaf) -> bool: + """Should `line` be immediately split with `delimiter_split()` after RHS?""" + + if not (opening_bracket.parent and opening_bracket.value in "[{("): + return False + + # We're essentially checking if the body is delimited by commas and there's more + # than one of them (we're excluding the trailing comma and if the delimiter priority + # is still commas, that means there's more). + exclude = set() + trailing_comma = False + try: + last_leaf = line.leaves[-1] + if last_leaf.type == token.COMMA: + trailing_comma = True + exclude.add(id(last_leaf)) + max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude) + except (IndexError, ValueError): + return False + + return max_priority == COMMA_PRIORITY and ( + (line.mode.magic_trailing_comma and trailing_comma) + # always explode imports + or opening_bracket.parent.type in {syms.atom, syms.import_from} + ) + + +def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[LeafID]]: + """Generate sets of closing bracket IDs that should be omitted in a RHS. + + Brackets can be omitted if the entire trailer up to and including + a preceding closing bracket fits in one line. + + Yielded sets are cumulative (contain results of previous yields, too). First + set is empty, unless the line should explode, in which case bracket pairs until + the one that needs to explode are omitted. + """ + + omit: Set[LeafID] = set() + if not line.magic_trailing_comma: + yield omit + + length = 4 * line.depth + opening_bracket: Optional[Leaf] = None + closing_bracket: Optional[Leaf] = None + inner_brackets: Set[LeafID] = set() + for index, leaf, leaf_length in line.enumerate_with_length(reversed=True): + length += leaf_length + if length > line_length: + break + + has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix) + if leaf.type == STANDALONE_COMMENT or has_inline_comment: + break + + if opening_bracket: + if leaf is opening_bracket: + opening_bracket = None + elif leaf.type in CLOSING_BRACKETS: + prev = line.leaves[index - 1] if index > 0 else None + if ( + prev + and prev.type == token.COMMA + and leaf.opening_bracket is not None + and not is_one_sequence_between( + leaf.opening_bracket, leaf, line.leaves + ) + ): + # Never omit bracket pairs with trailing commas. + # We need to explode on those. + break + + inner_brackets.add(id(leaf)) + elif leaf.type in CLOSING_BRACKETS: + prev = line.leaves[index - 1] if index > 0 else None + if prev and prev.type in OPENING_BRACKETS: + # Empty brackets would fail a split so treat them as "inner" + # brackets (e.g. only add them to the `omit` set if another + # pair of brackets was good enough. + inner_brackets.add(id(leaf)) + continue + + if closing_bracket: + omit.add(id(closing_bracket)) + omit.update(inner_brackets) + inner_brackets.clear() + yield omit + + if ( + prev + and prev.type == token.COMMA + and leaf.opening_bracket is not None + and not is_one_sequence_between(leaf.opening_bracket, leaf, line.leaves) + ): + # Never omit bracket pairs with trailing commas. + # We need to explode on those. + break + + if leaf.value: + opening_bracket = leaf.opening_bracket + closing_bracket = leaf + + +def run_transformer( + line: Line, + transform: Transformer, + mode: Mode, + features: Collection[Feature], + *, + line_str: str = "", +) -> List[Line]: + if not line_str: + line_str = line_to_string(line) + result: List[Line] = [] + for transformed_line in transform(line, features): + if str(transformed_line).strip("\n") == line_str: + raise CannotTransform("Line transformer returned an unchanged result") + + result.extend(transform_line(transformed_line, mode=mode, features=features)) + + features_set = set(features) + if ( + Feature.FORCE_OPTIONAL_PARENTHESES in features_set + or transform.__class__.__name__ != "rhs" + or not line.bracket_tracker.invisible + or any(bracket.value for bracket in line.bracket_tracker.invisible) + or line.contains_multiline_strings() + or result[0].contains_uncollapsable_type_comments() + or result[0].contains_unsplittable_type_ignore() + or is_line_short_enough(result[0], line_length=mode.line_length) + # If any leaves have no parents (which _can_ occur since + # `transform(line)` potentially destroys the line's underlying node + # structure), then we can't proceed. Doing so would cause the below + # call to `append_leaves()` to fail. + or any(leaf.parent is None for leaf in line.leaves) + ): + return result + + line_copy = line.clone() + append_leaves(line_copy, line, line.leaves) + features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES} + second_opinion = run_transformer( + line_copy, transform, mode, features_fop, line_str=line_str + ) + if all( + is_line_short_enough(ln, line_length=mode.line_length) for ln in second_opinion + ): + result = second_opinion + return result diff --git a/.venv/lib/python3.11/site-packages/black/lines.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/lines.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..4ed8ff5824950af5c29cdf586e7af3f747b1efbc GIT binary patch literal 8256 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 L@310KxBY(rf97EF literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/lines.py b/.venv/lib/python3.11/site-packages/black/lines.py new file mode 100644 index 00000000..ec6ef5d9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/lines.py @@ -0,0 +1,868 @@ +import itertools +import sys +from dataclasses import dataclass, field +from typing import ( + Callable, + Dict, + Iterator, + List, + Optional, + Sequence, + Tuple, + TypeVar, + cast, +) + +from black.brackets import DOT_PRIORITY, BracketTracker +from black.mode import Mode +from black.nodes import ( + BRACKETS, + CLOSING_BRACKETS, + OPENING_BRACKETS, + STANDALONE_COMMENT, + TEST_DESCENDANTS, + child_towards, + is_import, + is_multiline_string, + is_one_sequence_between, + is_type_comment, + replace_child, + syms, + whitespace, +) +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + +# types +T = TypeVar("T") +Index = int +LeafID = int + + +@dataclass +class Line: + """Holds leaves and comments. Can be printed with `str(line)`.""" + + mode: Mode + depth: int = 0 + leaves: List[Leaf] = field(default_factory=list) + # keys ordered like `leaves` + comments: Dict[LeafID, List[Leaf]] = field(default_factory=dict) + bracket_tracker: BracketTracker = field(default_factory=BracketTracker) + inside_brackets: bool = False + should_split_rhs: bool = False + magic_trailing_comma: Optional[Leaf] = None + + def append( + self, leaf: Leaf, preformatted: bool = False, track_bracket: bool = False + ) -> None: + """Add a new `leaf` to the end of the line. + + Unless `preformatted` is True, the `leaf` will receive a new consistent + whitespace prefix and metadata applied by :class:`BracketTracker`. + Trailing commas are maybe removed, unpacked for loop variables are + demoted from being delimiters. + + Inline comments are put aside. + """ + has_value = leaf.type in BRACKETS or bool(leaf.value.strip()) + if not has_value: + return + + if token.COLON == leaf.type and self.is_class_paren_empty: + del self.leaves[-2:] + if self.leaves and not preformatted: + # Note: at this point leaf.prefix should be empty except for + # imports, for which we only preserve newlines. + leaf.prefix += whitespace( + leaf, complex_subscript=self.is_complex_subscript(leaf) + ) + if self.inside_brackets or not preformatted or track_bracket: + self.bracket_tracker.mark(leaf) + if self.mode.magic_trailing_comma: + if self.has_magic_trailing_comma(leaf): + self.magic_trailing_comma = leaf + elif self.has_magic_trailing_comma(leaf, ensure_removable=True): + self.remove_trailing_comma() + if not self.append_comment(leaf): + self.leaves.append(leaf) + + def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None: + """Like :func:`append()` but disallow invalid standalone comment structure. + + Raises ValueError when any `leaf` is appended after a standalone comment + or when a standalone comment is not the first leaf on the line. + """ + if self.bracket_tracker.depth == 0: + if self.is_comment: + raise ValueError("cannot append to standalone comments") + + if self.leaves and leaf.type == STANDALONE_COMMENT: + raise ValueError( + "cannot append standalone comments to a populated line" + ) + + self.append(leaf, preformatted=preformatted) + + @property + def is_comment(self) -> bool: + """Is this line a standalone comment?""" + return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT + + @property + def is_decorator(self) -> bool: + """Is this line a decorator?""" + return bool(self) and self.leaves[0].type == token.AT + + @property + def is_import(self) -> bool: + """Is this an import line?""" + return bool(self) and is_import(self.leaves[0]) + + @property + def is_class(self) -> bool: + """Is this line a class definition?""" + return ( + bool(self) + and self.leaves[0].type == token.NAME + and self.leaves[0].value == "class" + ) + + @property + def is_stub_class(self) -> bool: + """Is this line a class definition with a body consisting only of "..."?""" + return self.is_class and self.leaves[-3:] == [ + Leaf(token.DOT, ".") for _ in range(3) + ] + + @property + def is_def(self) -> bool: + """Is this a function definition? (Also returns True for async defs.)""" + try: + first_leaf = self.leaves[0] + except IndexError: + return False + + try: + second_leaf: Optional[Leaf] = self.leaves[1] + except IndexError: + second_leaf = None + return (first_leaf.type == token.NAME and first_leaf.value == "def") or ( + first_leaf.type == token.ASYNC + and second_leaf is not None + and second_leaf.type == token.NAME + and second_leaf.value == "def" + ) + + @property + def is_class_paren_empty(self) -> bool: + """Is this a class with no base classes but using parentheses? + + Those are unnecessary and should be removed. + """ + return ( + bool(self) + and len(self.leaves) == 4 + and self.is_class + and self.leaves[2].type == token.LPAR + and self.leaves[2].value == "(" + and self.leaves[3].type == token.RPAR + and self.leaves[3].value == ")" + ) + + @property + def is_triple_quoted_string(self) -> bool: + """Is the line a triple quoted string?""" + return ( + bool(self) + and self.leaves[0].type == token.STRING + and self.leaves[0].value.startswith(('"""', "'''")) + ) + + @property + def opens_block(self) -> bool: + """Does this line open a new level of indentation.""" + if len(self.leaves) == 0: + return False + return self.leaves[-1].type == token.COLON + + def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: + """If so, needs to be split before emitting.""" + for leaf in self.leaves: + if leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit: + return True + + return False + + def contains_uncollapsable_type_comments(self) -> bool: + ignored_ids = set() + try: + last_leaf = self.leaves[-1] + ignored_ids.add(id(last_leaf)) + if last_leaf.type == token.COMMA or ( + last_leaf.type == token.RPAR and not last_leaf.value + ): + # When trailing commas or optional parens are inserted by Black for + # consistency, comments after the previous last element are not moved + # (they don't have to, rendering will still be correct). So we ignore + # trailing commas and invisible. + last_leaf = self.leaves[-2] + ignored_ids.add(id(last_leaf)) + except IndexError: + return False + + # A type comment is uncollapsable if it is attached to a leaf + # that isn't at the end of the line (since that could cause it + # to get associated to a different argument) or if there are + # comments before it (since that could cause it to get hidden + # behind a comment. + comment_seen = False + for leaf_id, comments in self.comments.items(): + for comment in comments: + if is_type_comment(comment): + if comment_seen or ( + not is_type_comment(comment, " ignore") + and leaf_id not in ignored_ids + ): + return True + + comment_seen = True + + return False + + def contains_unsplittable_type_ignore(self) -> bool: + if not self.leaves: + return False + + # If a 'type: ignore' is attached to the end of a line, we + # can't split the line, because we can't know which of the + # subexpressions the ignore was meant to apply to. + # + # We only want this to apply to actual physical lines from the + # original source, though: we don't want the presence of a + # 'type: ignore' at the end of a multiline expression to + # justify pushing it all onto one line. Thus we + # (unfortunately) need to check the actual source lines and + # only report an unsplittable 'type: ignore' if this line was + # one line in the original code. + + # Grab the first and last line numbers, skipping generated leaves + first_line = next((leaf.lineno for leaf in self.leaves if leaf.lineno != 0), 0) + last_line = next( + (leaf.lineno for leaf in reversed(self.leaves) if leaf.lineno != 0), 0 + ) + + if first_line == last_line: + # We look at the last two leaves since a comma or an + # invisible paren could have been added at the end of the + # line. + for node in self.leaves[-2:]: + for comment in self.comments.get(id(node), []): + if is_type_comment(comment, " ignore"): + return True + + return False + + def contains_multiline_strings(self) -> bool: + return any(is_multiline_string(leaf) for leaf in self.leaves) + + def has_magic_trailing_comma( + self, closing: Leaf, ensure_removable: bool = False + ) -> bool: + """Return True if we have a magic trailing comma, that is when: + - there's a trailing comma here + - it's not a one-tuple + - it's not a single-element subscript + Additionally, if ensure_removable: + - it's not from square bracket indexing + (specifically, single-element square bracket indexing) + """ + if not ( + closing.type in CLOSING_BRACKETS + and self.leaves + and self.leaves[-1].type == token.COMMA + ): + return False + + if closing.type == token.RBRACE: + return True + + if closing.type == token.RSQB: + if ( + closing.parent + and closing.parent.type == syms.trailer + and closing.opening_bracket + and is_one_sequence_between( + closing.opening_bracket, + closing, + self.leaves, + brackets=(token.LSQB, token.RSQB), + ) + ): + return False + + if not ensure_removable: + return True + + comma = self.leaves[-1] + if comma.parent is None: + return False + return ( + comma.parent.type != syms.subscriptlist + or closing.opening_bracket is None + or not is_one_sequence_between( + closing.opening_bracket, + closing, + self.leaves, + brackets=(token.LSQB, token.RSQB), + ) + ) + + if self.is_import: + return True + + if closing.opening_bracket is not None and not is_one_sequence_between( + closing.opening_bracket, closing, self.leaves + ): + return True + + return False + + def append_comment(self, comment: Leaf) -> bool: + """Add an inline or standalone comment to the line.""" + if ( + comment.type == STANDALONE_COMMENT + and self.bracket_tracker.any_open_brackets() + ): + comment.prefix = "" + return False + + if comment.type != token.COMMENT: + return False + + if not self.leaves: + comment.type = STANDALONE_COMMENT + comment.prefix = "" + return False + + last_leaf = self.leaves[-1] + if ( + last_leaf.type == token.RPAR + and not last_leaf.value + and last_leaf.parent + and len(list(last_leaf.parent.leaves())) <= 3 + and not is_type_comment(comment) + ): + # Comments on an optional parens wrapping a single leaf should belong to + # the wrapped node except if it's a type comment. Pinning the comment like + # this avoids unstable formatting caused by comment migration. + if len(self.leaves) < 2: + comment.type = STANDALONE_COMMENT + comment.prefix = "" + return False + + last_leaf = self.leaves[-2] + self.comments.setdefault(id(last_leaf), []).append(comment) + return True + + def comments_after(self, leaf: Leaf) -> List[Leaf]: + """Generate comments that should appear directly after `leaf`.""" + return self.comments.get(id(leaf), []) + + def remove_trailing_comma(self) -> None: + """Remove the trailing comma and moves the comments attached to it.""" + trailing_comma = self.leaves.pop() + trailing_comma_comments = self.comments.pop(id(trailing_comma), []) + self.comments.setdefault(id(self.leaves[-1]), []).extend( + trailing_comma_comments + ) + + def is_complex_subscript(self, leaf: Leaf) -> bool: + """Return True iff `leaf` is part of a slice with non-trivial exprs.""" + open_lsqb = self.bracket_tracker.get_open_lsqb() + if open_lsqb is None: + return False + + subscript_start = open_lsqb.next_sibling + + if isinstance(subscript_start, Node): + if subscript_start.type == syms.listmaker: + return False + + if subscript_start.type == syms.subscriptlist: + subscript_start = child_towards(subscript_start, leaf) + return subscript_start is not None and any( + n.type in TEST_DESCENDANTS for n in subscript_start.pre_order() + ) + + def enumerate_with_length( + self, reversed: bool = False + ) -> Iterator[Tuple[Index, Leaf, int]]: + """Return an enumeration of leaves with their length. + + Stops prematurely on multiline strings and standalone comments. + """ + op = cast( + Callable[[Sequence[Leaf]], Iterator[Tuple[Index, Leaf]]], + enumerate_reversed if reversed else enumerate, + ) + for index, leaf in op(self.leaves): + length = len(leaf.prefix) + len(leaf.value) + if "\n" in leaf.value: + return # Multiline strings, we can't continue. + + for comment in self.comments_after(leaf): + length += len(comment.value) + + yield index, leaf, length + + def clone(self) -> "Line": + return Line( + mode=self.mode, + depth=self.depth, + inside_brackets=self.inside_brackets, + should_split_rhs=self.should_split_rhs, + magic_trailing_comma=self.magic_trailing_comma, + ) + + def __str__(self) -> str: + """Render the line.""" + if not self: + return "\n" + + indent = " " * self.depth + leaves = iter(self.leaves) + first = next(leaves) + res = f"{first.prefix}{indent}{first.value}" + for leaf in leaves: + res += str(leaf) + for comment in itertools.chain.from_iterable(self.comments.values()): + res += str(comment) + + return res + "\n" + + def __bool__(self) -> bool: + """Return True if the line has leaves or comments.""" + return bool(self.leaves or self.comments) + + +@dataclass +class LinesBlock: + """Class that holds information about a block of formatted lines. + + This is introduced so that the EmptyLineTracker can look behind the standalone + comments and adjust their empty lines for class or def lines. + """ + + mode: Mode + previous_block: Optional["LinesBlock"] + original_line: Line + before: int = 0 + content_lines: List[str] = field(default_factory=list) + after: int = 0 + + def all_lines(self) -> List[str]: + empty_line = str(Line(mode=self.mode)) + return ( + [empty_line * self.before] + self.content_lines + [empty_line * self.after] + ) + + +@dataclass +class EmptyLineTracker: + """Provides a stateful method that returns the number of potential extra + empty lines needed before and after the currently processed line. + + Note: this tracker works on lines that haven't been split yet. It assumes + the prefix of the first leaf consists of optional newlines. Those newlines + are consumed by `maybe_empty_lines()` and included in the computation. + """ + + mode: Mode + previous_line: Optional[Line] = None + previous_block: Optional[LinesBlock] = None + previous_defs: List[int] = field(default_factory=list) + semantic_leading_comment: Optional[LinesBlock] = None + + def maybe_empty_lines(self, current_line: Line) -> LinesBlock: + """Return the number of extra empty lines before and after the `current_line`. + + This is for separating `def`, `async def` and `class` with extra empty + lines (two on module-level). + """ + before, after = self._maybe_empty_lines(current_line) + previous_after = self.previous_block.after if self.previous_block else 0 + before = ( + # Black should not insert empty lines at the beginning + # of the file + 0 + if self.previous_line is None + else before - previous_after + ) + block = LinesBlock( + mode=self.mode, + previous_block=self.previous_block, + original_line=current_line, + before=before, + after=after, + ) + + # Maintain the semantic_leading_comment state. + if current_line.is_comment: + if self.previous_line is None or ( + not self.previous_line.is_decorator + # `or before` means this comment already has an empty line before + and (not self.previous_line.is_comment or before) + and (self.semantic_leading_comment is None or before) + ): + self.semantic_leading_comment = block + # `or before` means this decorator already has an empty line before + elif not current_line.is_decorator or before: + self.semantic_leading_comment = None + + self.previous_line = current_line + self.previous_block = block + return block + + def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: + max_allowed = 1 + if current_line.depth == 0: + max_allowed = 1 if self.mode.is_pyi else 2 + if current_line.leaves: + # Consume the first leaf's extra newlines. + first_leaf = current_line.leaves[0] + before = first_leaf.prefix.count("\n") + before = min(before, max_allowed) + first_leaf.prefix = "" + else: + before = 0 + depth = current_line.depth + while self.previous_defs and self.previous_defs[-1] >= depth: + if self.mode.is_pyi: + assert self.previous_line is not None + if depth and not current_line.is_def and self.previous_line.is_def: + # Empty lines between attributes and methods should be preserved. + before = min(1, before) + elif depth: + before = 0 + else: + before = 1 + else: + if depth: + before = 1 + elif ( + not depth + and self.previous_defs[-1] + and current_line.leaves[-1].type == token.COLON + and ( + current_line.leaves[0].value + not in ("with", "try", "for", "while", "if", "match") + ) + ): + # We shouldn't add two newlines between an indented function and + # a dependent non-indented clause. This is to avoid issues with + # conditional function definitions that are technically top-level + # and therefore get two trailing newlines, but look weird and + # inconsistent when they're followed by elif, else, etc. This is + # worse because these functions only get *one* preceding newline + # already. + before = 1 + else: + before = 2 + self.previous_defs.pop() + if current_line.is_decorator or current_line.is_def or current_line.is_class: + return self._maybe_empty_lines_for_class_or_def(current_line, before) + + if ( + self.previous_line + and self.previous_line.is_import + and not current_line.is_import + and depth == self.previous_line.depth + ): + return (before or 1), 0 + + if ( + self.previous_line + and self.previous_line.is_class + and current_line.is_triple_quoted_string + ): + return before, 1 + + if self.previous_line and self.previous_line.opens_block: + return 0, 0 + return before, 0 + + def _maybe_empty_lines_for_class_or_def( + self, current_line: Line, before: int + ) -> Tuple[int, int]: + if not current_line.is_decorator: + self.previous_defs.append(current_line.depth) + if self.previous_line is None: + # Don't insert empty lines before the first line in the file. + return 0, 0 + + if self.previous_line.is_decorator: + if self.mode.is_pyi and current_line.is_stub_class: + # Insert an empty line after a decorated stub class + return 0, 1 + + return 0, 0 + + if self.previous_line.depth < current_line.depth and ( + self.previous_line.is_class or self.previous_line.is_def + ): + return 0, 0 + + comment_to_add_newlines: Optional[LinesBlock] = None + if ( + self.previous_line.is_comment + and self.previous_line.depth == current_line.depth + and before == 0 + ): + slc = self.semantic_leading_comment + if ( + slc is not None + and slc.previous_block is not None + and not slc.previous_block.original_line.is_class + and not slc.previous_block.original_line.opens_block + and slc.before <= 1 + ): + comment_to_add_newlines = slc + else: + return 0, 0 + + if self.mode.is_pyi: + if current_line.is_class or self.previous_line.is_class: + if self.previous_line.depth < current_line.depth: + newlines = 0 + elif self.previous_line.depth > current_line.depth: + newlines = 1 + elif current_line.is_stub_class and self.previous_line.is_stub_class: + # No blank line between classes with an empty body + newlines = 0 + else: + newlines = 1 + elif ( + current_line.is_def or current_line.is_decorator + ) and not self.previous_line.is_def: + if current_line.depth: + # In classes empty lines between attributes and methods should + # be preserved. + newlines = min(1, before) + else: + # Blank line between a block of functions (maybe with preceding + # decorators) and a block of non-functions + newlines = 1 + elif self.previous_line.depth > current_line.depth: + newlines = 1 + else: + newlines = 0 + else: + newlines = 1 if current_line.depth else 2 + if comment_to_add_newlines is not None: + previous_block = comment_to_add_newlines.previous_block + if previous_block is not None: + comment_to_add_newlines.before = ( + max(comment_to_add_newlines.before, newlines) - previous_block.after + ) + newlines = 0 + return newlines, 0 + + +def enumerate_reversed(sequence: Sequence[T]) -> Iterator[Tuple[Index, T]]: + """Like `reversed(enumerate(sequence))` if that were possible.""" + index = len(sequence) - 1 + for element in reversed(sequence): + yield (index, element) + index -= 1 + + +def append_leaves( + new_line: Line, old_line: Line, leaves: List[Leaf], preformatted: bool = False +) -> None: + """ + Append leaves (taken from @old_line) to @new_line, making sure to fix the + underlying Node structure where appropriate. + + All of the leaves in @leaves are duplicated. The duplicates are then + appended to @new_line and used to replace their originals in the underlying + Node structure. Any comments attached to the old leaves are reattached to + the new leaves. + + Pre-conditions: + set(@leaves) is a subset of set(@old_line.leaves). + """ + for old_leaf in leaves: + new_leaf = Leaf(old_leaf.type, old_leaf.value) + replace_child(old_leaf, new_leaf) + new_line.append(new_leaf, preformatted=preformatted) + + for comment_leaf in old_line.comments_after(old_leaf): + new_line.append(comment_leaf, preformatted=True) + + +def is_line_short_enough(line: Line, *, line_length: int, line_str: str = "") -> bool: + """Return True if `line` is no longer than `line_length`. + + Uses the provided `line_str` rendering, if any, otherwise computes a new one. + """ + if not line_str: + line_str = line_to_string(line) + return ( + len(line_str) <= line_length + and "\n" not in line_str # multiline strings + and not line.contains_standalone_comments() + ) + + +def can_be_split(line: Line) -> bool: + """Return False if the line cannot be split *for sure*. + + This is not an exhaustive search but a cheap heuristic that we can use to + avoid some unfortunate formattings (mostly around wrapping unsplittable code + in unnecessary parentheses). + """ + leaves = line.leaves + if len(leaves) < 2: + return False + + if leaves[0].type == token.STRING and leaves[1].type == token.DOT: + call_count = 0 + dot_count = 0 + next = leaves[-1] + for leaf in leaves[-2::-1]: + if leaf.type in OPENING_BRACKETS: + if next.type not in CLOSING_BRACKETS: + return False + + call_count += 1 + elif leaf.type == token.DOT: + dot_count += 1 + elif leaf.type == token.NAME: + if not (next.type == token.DOT or next.type in OPENING_BRACKETS): + return False + + elif leaf.type not in CLOSING_BRACKETS: + return False + + if dot_count > 1 and call_count > 1: + return False + + return True + + +def can_omit_invisible_parens( + line: Line, + line_length: int, +) -> bool: + """Does `line` have a shape safe to reformat without optional parens around it? + + Returns True for only a subset of potentially nice looking formattings but + the point is to not return false positives that end up producing lines that + are too long. + """ + bt = line.bracket_tracker + if not bt.delimiters: + # Without delimiters the optional parentheses are useless. + return True + + max_priority = bt.max_delimiter_priority() + if bt.delimiter_count_with_priority(max_priority) > 1: + # With more than one delimiter of a kind the optional parentheses read better. + return False + + if max_priority == DOT_PRIORITY: + # A single stranded method call doesn't require optional parentheses. + return True + + assert len(line.leaves) >= 2, "Stranded delimiter" + + # With a single delimiter, omit if the expression starts or ends with + # a bracket. + first = line.leaves[0] + second = line.leaves[1] + if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS: + if _can_omit_opening_paren(line, first=first, line_length=line_length): + return True + + # Note: we are not returning False here because a line might have *both* + # a leading opening bracket and a trailing closing bracket. If the + # opening bracket doesn't match our rule, maybe the closing will. + + penultimate = line.leaves[-2] + last = line.leaves[-1] + + if ( + last.type == token.RPAR + or last.type == token.RBRACE + or ( + # don't use indexing for omitting optional parentheses; + # it looks weird + last.type == token.RSQB + and last.parent + and last.parent.type != syms.trailer + ) + ): + if penultimate.type in OPENING_BRACKETS: + # Empty brackets don't help. + return False + + if is_multiline_string(first): + # Additional wrapping of a multiline string in this situation is + # unnecessary. + return True + + if _can_omit_closing_paren(line, last=last, line_length=line_length): + return True + + return False + + +def _can_omit_opening_paren(line: Line, *, first: Leaf, line_length: int) -> bool: + """See `can_omit_invisible_parens`.""" + remainder = False + length = 4 * line.depth + _index = -1 + for _index, leaf, leaf_length in line.enumerate_with_length(): + if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first: + remainder = True + if remainder: + length += leaf_length + if length > line_length: + break + + if leaf.type in OPENING_BRACKETS: + # There are brackets we can further split on. + remainder = False + + else: + # checked the entire string and line length wasn't exceeded + if len(line.leaves) == _index + 1: + return True + + return False + + +def _can_omit_closing_paren(line: Line, *, last: Leaf, line_length: int) -> bool: + """See `can_omit_invisible_parens`.""" + length = 4 * line.depth + seen_other_brackets = False + for _index, leaf, leaf_length in line.enumerate_with_length(): + length += leaf_length + if leaf is last.opening_bracket: + if seen_other_brackets or length <= line_length: + return True + + elif leaf.type in OPENING_BRACKETS: + # There are brackets we can further split on. + seen_other_brackets = True + + return False + + +def line_to_string(line: Line) -> str: + """Returns the string representation of @line. + + WARNING: This is known to be computationally expensive. + """ + return str(line).strip("\n") diff --git a/.venv/lib/python3.11/site-packages/black/mode.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/mode.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..5d0922b4421e4cad01edc509341711bda4058e22 GIT binary patch literal 8248 zcmeHMYit}>6~61WlTG8=+t4H?iIfqzU?Vbi{HjajcKuj;Z0yu^Y#tTvWOiq5FWraP zolWiVvXnNhiz0>ms0zPKPyyu+0f~y1Kiecq!w-;oREz2v?wou4&ERlB*ymIEV6-rHtg~fbrOz;%{$( zW4{!qBGu0WE$7DoTp=AN>D&Sz1N>u7MXLWAs9)?60l!it{jzK9WvCBq^X4TaTo zQtc3W#561e#uQ4Y?I8h_FvY;|U{9~!qPD2*G#>?0Av{HYwgj;&P~|<6*%yT^T_OHL zhJ3E@G>_7}3OYX+_Y>}SGP=C{bbDt2e zqy2}vbOazQ6bFv)HkSqm?(9#q19zU=oetdDu6F8epZ2Y)pY2eD_Dt5QS~~(QXs53D zF54~T+Ao19_rMkStvwik8uBwJTwlpSdEiy#ajMprCI3_8aR}>K$zMPoyS{!=^5>Dq zF0U^Eubrt{0zf;{bq>gc*0`xHu4<N&$9NL&5ShO{f-Zfe+$Oi;?>*@v|s3I1Ur%Wq;{(7O%Owqi>p>Ow0~op zBGx9MOKUUmGkLX2fTHJbm(glUtv&cObXmaEbX^2RVSNHR_pJ1vwf0Sb{dWNdU%BPU z_0NNY+Jg(Izy4XM7tGI&g67u&U%Pn+1`h-KxR&!}eA?pcYu`iUOxIUIdid7b_et)3 zJhyT<`#5xC0R#f=yMX591$)D`#{~rYu*)7$xz@O}9ii5T9yHC4wn%exsNI;FYH2lf zJw87>9~SQ;Q}Ha)Lop-#B%BrUbP%5X-Xrzk8y9%x-?>}>Xfq6X2Iwr%1)x~)+l9q* za!h#U{GR>Q+kIzY*PtzaTV4d)<3L4?zrUt#sCMU5z6J5fz9XM%t(V6++V{beg<;9F zxVuIk1&DtMo;4Q^(A{!(2JX(l-5L15oB@u1INsq&;JAlln=2%!Cd%FrLD)*Cyge)G*^~m9~KUnI%04E@k2N;PWE;e~t9v(oD-bEuZrk4j9_470;E`_viC7#rpR_Jf=7y zS@*(C*H;$*h~V?ZY2=0IuiQTLirs?mTgCctYo_OoTzE!{}v*cPAJm(N!cHt`F?(?-z`Y-g8&xd0YFP+bmsZeJO{rhHz8exRUEkY)W46h?538Z0o-%ou zI>O!St^%$|U&TK|fS1eXJSOpCUF0SK-z7@lqwp5`7T`*$8*yq2{qF!?E?@ML)Gy61 zT_F8s`Vh%G4Yc|+`tye^@V^5tFHWv8_~AoYE>9)GDJyMdrl!;|q-%zH z$MoUBW8;tuD&(7VE3StzcqMMm`d>%|3oSltwsy12iS$s}3hdZaAe6XV_l!?a7CV3-m z!uzP~eE(SYNKg+R?Zfn$917RXbW)!&5+>%W`o4Iydt|T|G~nZ=2Q}R`)B45$g47y2 z7#M!Er+Zj`w7>sYa9khn?imh(+xN@FQO?Ve5VO;FC7Co=s;_)v&6$gnxibRt$-1KC zTAAdY?CXcf3B1Q*UDiSl$fNHZbOUO)vP_4)u)Mf5dO(kV2QH{*d9+@%0mO0zP-dDB- z*S{PFH8jO`z>DmY5s!wUA4%{IKdNjJQj|i4EgXk!>3V7Y3l_f{_-b=p%5euODpoTFr+jE@7R3T07KkG4_06E6o%yT@(lnOAZhMsDoN(B{_bR6O`x5A2!M-|68W^X2fE+PB?o@_4=f#--$Du;r!pO4L!#Urrsv3>*N@`}ru8m+ zcl%2&dyadVx?gc#KX!WoDmC!%{@^%_|8M8kXP)WHU|Z|3&&Sc|6bkFvT!Cl&5(scE zxP6YdeIH=Y{$6+4b6oEKkoLa^d)zYIKF9C;f7vW86wf>NoA=S5T bool: + return all(feature in VERSION_TO_FEATURES[version] for version in target_versions) + + +class Preview(Enum): + """Individual preview style features.""" + + hex_codes_in_unicode_sequences = auto() + prefer_splitting_right_hand_side_of_assignments = auto() + # NOTE: string_processing requires wrap_long_dict_values_in_parens + # for https://github.com/psf/black/issues/3117 to be fixed. + string_processing = auto() + parenthesize_conditional_expressions = auto() + skip_magic_trailing_comma_in_subscript = auto() + wrap_long_dict_values_in_parens = auto() + wrap_multiple_context_managers_in_parens = auto() + + +class Deprecated(UserWarning): + """Visible deprecation warning.""" + + +@dataclass +class Mode: + target_versions: Set[TargetVersion] = field(default_factory=set) + line_length: int = DEFAULT_LINE_LENGTH + string_normalization: bool = True + is_pyi: bool = False + is_ipynb: bool = False + skip_source_first_line: bool = False + magic_trailing_comma: bool = True + experimental_string_processing: bool = False + python_cell_magics: Set[str] = field(default_factory=set) + preview: bool = False + + def __post_init__(self) -> None: + if self.experimental_string_processing: + warn( + ( + "`experimental string processing` has been included in `preview`" + " and deprecated. Use `preview` instead." + ), + Deprecated, + ) + + def __contains__(self, feature: Preview) -> bool: + """ + Provide `Preview.FEATURE in Mode` syntax that mirrors the ``preview`` flag. + + The argument is not checked and features are not differentiated. + They only exist to make development easier by clarifying intent. + """ + if feature is Preview.string_processing: + return self.preview or self.experimental_string_processing + return self.preview + + def get_cache_key(self) -> str: + if self.target_versions: + version_str = ",".join( + str(version.value) + for version in sorted(self.target_versions, key=attrgetter("value")) + ) + else: + version_str = "-" + parts = [ + version_str, + str(self.line_length), + str(int(self.string_normalization)), + str(int(self.is_pyi)), + str(int(self.is_ipynb)), + str(int(self.skip_source_first_line)), + str(int(self.magic_trailing_comma)), + str(int(self.experimental_string_processing)), + str(int(self.preview)), + sha256((",".join(sorted(self.python_cell_magics))).encode()).hexdigest(), + ] + return ".".join(parts) diff --git a/.venv/lib/python3.11/site-packages/black/nodes.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/nodes.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..a7761415249119ae22fdd6d5670d8d708798c1f2 GIT binary patch literal 8256 zcmeHMT}&L;6~4=Yy@@dp8pp)8Ck+{fodvepAG$m1 zfcuAJCpE1sS$;^Wyp$%bR7HJoD>YKuJ`g)iqP&zgP3wnNMNUy6jTGFd7OC27&$)BX zGJAJPqCWJczG&v$^PQjjyLZk#d^0rEU*_`(E`IS@f!yK_7YULecqdhW1VyLV3GV}9 zztpYR5*5|{6>uS@LTD1eHn54!-I4{W$qG}W%*98DvK_K$S6AiA>#8(aVY);^VM@)5 za?rx(HIl2eUV0INDe7VwS#@lzkCP*nvOZI`dkE}k3Z(2$`OYuiy7;xLU-`m;7tdd>di#xctL}eVT=>cA3j-i7Sq=cIm5{s$7>~^>zPJsJ z{nEUORDTX=DL)qA8tHgR=Qem4@VmTr0Y<9(nBg`X$Lfi#&FD zeGz!$e8nOF#`&&`Ku#Kso5sSbab~mX@v^Zi(6QT52p#+TSZ)Q|2mP3%NgY&4rehlhc<`+gl^Xq`Gz4ZYM9tQSFBNxa9jD=U%zK_WHuCIdh@Qt@C+G7ZB_tianrmt#N5PCR*>i*S0&_qRq_{?bhUEORH^~ ziMg4%i1-khj%SfM5w{|b!CB#+4!-^VBlX}L7kK60xm*xvGYoke=nT+#pjhzRg~fL| zEPS&yzWtRu0~cV|pe=q|z6-YFKt)wee^uSl>Rpcq=EVd1j@;8)FOPGy?}KLvh9%F? z?kaf{ApQtEYYGm~&2n=FZqC5X8Th}P0git--r-5$xQAmKjH$3xx+EXh(zPug&+_$< zmt=tuGKbJ@kf=gMrJ@~xH{=Y{0@M)7g&*KL1KOp)0 z#OM1-{{Gj5@6BqvpQ8z%BzlVIlSH2)I?&sDzt#XZ&PJ_G@6_A1mgd9l%^fYRT0=N$ zYlh{x_@TD8MiGfy$tmqrG@Xg1l3J_YrnhJfjy08O3WtV5-6uk3Yg?0@a#{|_>qw*8 zDTVh#1kU$(ugDJ(GVh~8+%I_l6yoKA_gNucA$YwC@g0KKxe(tec)t|lf&9KI#Eb8V zDEEt7MUdvVP=8lGFH?vY-z!}|yTu;fxE=$)sLJOvJTSCdEzXtHugT|U3iWS;cuey` zvhIf)ZJ;Fn3Bl)!*T@giU#WfQ7kdQXw+i*)Hch|lLVR!0{T=20{Hd^|jlausaXq(% zxHUZId?osi=UW+^GKk=k$V*ITKLMT`5Ai~{Vd>Gu^VxA8~tY_UYtkz z9^j?&KR+gXnLb3E7y2dn;r+v1z;m!vfBv`)egklMabgEVYvDs#Dt{#+X(!`kCnxm? zWNVK0hRvbD6Qhs|+R8VXPQr{}#wi0DcFLTJrzWhpX*;QO#ti8(OwaBeortJ>rwX7D-H9O;~T?B0&1iEJ!x zH_01mle~}0-uH*QheKxQcpv7^upaEYuGh~>qnb8*p z2y$!eU|{IMp6(&@!T$aep;2?RyJsi_Za*v)M>#J~Ld?zIjm#;TG$q%!O=1o6WlBb_ zFkM%WT`Q5^lb!t-xxrx2o0fAkk3!GPB^+x4sFQZ-G}n?TC#p{+v$|Y{Lor)OZraLB z3*DYef~8BHv@4;=pnx$!mX5|PG@#l{+z~R@Aao}>3w4?O)YB=MOVp#&v`eOKuw~A* zaCeq#!SydkKn+c?9q=OiWF=w|=tl~?!%LSIVN5VR<-#AS$ z(|W~TZGTa*=lGYY`a|dSW4F&kr3xP29~_VI|L;_N=9#_#w$&c{d_0X#p|GCIZSjm> z1_6$r+vm7D@DcXx?^VT~= (3, 8): + from typing import Final +else: + from typing_extensions import Final +if sys.version_info >= (3, 10): + from typing import TypeGuard +else: + from typing_extensions import TypeGuard + +from mypy_extensions import mypyc_attr + +from black.cache import CACHE_DIR +from black.strings import has_triple_quotes +from blib2to3 import pygram +from blib2to3.pgen2 import token +from blib2to3.pytree import NL, Leaf, Node, type_repr + +pygram.initialize(CACHE_DIR) +syms: Final = pygram.python_symbols + + +# types +T = TypeVar("T") +LN = Union[Leaf, Node] +LeafID = int +NodeType = int + + +WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE} +STATEMENT: Final = { + syms.if_stmt, + syms.while_stmt, + syms.for_stmt, + syms.try_stmt, + syms.except_clause, + syms.with_stmt, + syms.funcdef, + syms.classdef, + syms.match_stmt, + syms.case_block, +} +STANDALONE_COMMENT: Final = 153 +token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT" +LOGIC_OPERATORS: Final = {"and", "or"} +COMPARATORS: Final = { + token.LESS, + token.GREATER, + token.EQEQUAL, + token.NOTEQUAL, + token.LESSEQUAL, + token.GREATEREQUAL, +} +MATH_OPERATORS: Final = { + token.VBAR, + token.CIRCUMFLEX, + token.AMPER, + token.LEFTSHIFT, + token.RIGHTSHIFT, + token.PLUS, + token.MINUS, + token.STAR, + token.SLASH, + token.DOUBLESLASH, + token.PERCENT, + token.AT, + token.TILDE, + token.DOUBLESTAR, +} +STARS: Final = {token.STAR, token.DOUBLESTAR} +VARARGS_SPECIALS: Final = STARS | {token.SLASH} +VARARGS_PARENTS: Final = { + syms.arglist, + syms.argument, # double star in arglist + syms.trailer, # single argument to call + syms.typedargslist, + syms.varargslist, # lambdas +} +UNPACKING_PARENTS: Final = { + syms.atom, # single element of a list or set literal + syms.dictsetmaker, + syms.listmaker, + syms.testlist_gexp, + syms.testlist_star_expr, + syms.subject_expr, + syms.pattern, +} +TEST_DESCENDANTS: Final = { + syms.test, + syms.lambdef, + syms.or_test, + syms.and_test, + syms.not_test, + syms.comparison, + syms.star_expr, + syms.expr, + syms.xor_expr, + syms.and_expr, + syms.shift_expr, + syms.arith_expr, + syms.trailer, + syms.term, + syms.power, +} +TYPED_NAMES: Final = {syms.tname, syms.tname_star} +ASSIGNMENTS: Final = { + "=", + "+=", + "-=", + "*=", + "@=", + "/=", + "%=", + "&=", + "|=", + "^=", + "<<=", + ">>=", + "**=", + "//=", +} + +IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist} +BRACKET: Final = { + token.LPAR: token.RPAR, + token.LSQB: token.RSQB, + token.LBRACE: token.RBRACE, +} +OPENING_BRACKETS: Final = set(BRACKET.keys()) +CLOSING_BRACKETS: Final = set(BRACKET.values()) +BRACKETS: Final = OPENING_BRACKETS | CLOSING_BRACKETS +ALWAYS_NO_SPACE: Final = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT} + +RARROW = 55 + + +@mypyc_attr(allow_interpreted_subclasses=True) +class Visitor(Generic[T]): + """Basic lib2to3 visitor that yields things of type `T` on `visit()`.""" + + def visit(self, node: LN) -> Iterator[T]: + """Main method to visit `node` and its children. + + It tries to find a `visit_*()` method for the given `node.type`, like + `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects. + If no dedicated `visit_*()` method is found, chooses `visit_default()` + instead. + + Then yields objects of type `T` from the selected visitor. + """ + if node.type < 256: + name = token.tok_name[node.type] + else: + name = str(type_repr(node.type)) + # We explicitly branch on whether a visitor exists (instead of + # using self.visit_default as the default arg to getattr) in order + # to save needing to create a bound method object and so mypyc can + # generate a native call to visit_default. + visitf = getattr(self, f"visit_{name}", None) + if visitf: + yield from visitf(node) + else: + yield from self.visit_default(node) + + def visit_default(self, node: LN) -> Iterator[T]: + """Default `visit_*()` implementation. Recurses to children of `node`.""" + if isinstance(node, Node): + for child in node.children: + yield from self.visit(child) + + +def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901 + """Return whitespace prefix if needed for the given `leaf`. + + `complex_subscript` signals whether the given leaf is part of a subscription + which has non-trivial arguments, like arithmetic expressions or function calls. + """ + NO: Final = "" + SPACE: Final = " " + DOUBLESPACE: Final = " " + t = leaf.type + p = leaf.parent + v = leaf.value + if t in ALWAYS_NO_SPACE: + return NO + + if t == token.COMMENT: + return DOUBLESPACE + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + if t == token.COLON and p.type not in { + syms.subscript, + syms.subscriptlist, + syms.sliceop, + }: + return NO + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + return NO + + if t == token.COLON: + if prevp.type == token.COLON: + return NO + + elif prevp.type != token.COMMA and not complex_subscript: + return NO + + return SPACE + + if prevp.type == token.EQUAL: + if prevp.parent: + if prevp.parent.type in { + syms.arglist, + syms.argument, + syms.parameters, + syms.varargslist, + }: + return NO + + elif prevp.parent.type == syms.typedargslist: + # A bit hacky: if the equal sign has whitespace, it means we + # previously found it's a typed argument. So, we're using + # that, too. + return prevp.prefix + + elif ( + prevp.type == token.STAR + and parent_type(prevp) == syms.star_expr + and parent_type(prevp.parent) == syms.subscriptlist + ): + # No space between typevar tuples. + return NO + + elif prevp.type in VARARGS_SPECIALS: + if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS): + return NO + + elif prevp.type == token.COLON: + if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}: + return SPACE if complex_subscript else NO + + elif ( + prevp.parent + and prevp.parent.type == syms.factor + and prevp.type in MATH_OPERATORS + ): + return NO + + elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator: + # no space in decorators + return NO + + elif prev.type in OPENING_BRACKETS: + return NO + + if p.type in {syms.parameters, syms.arglist}: + # untyped function signatures or calls + if not prev or prev.type != token.COMMA: + return NO + + elif p.type == syms.varargslist: + # lambdas + if prev and prev.type != token.COMMA: + return NO + + elif p.type == syms.typedargslist: + # typed function signatures + if not prev: + return NO + + if t == token.EQUAL: + if prev.type not in TYPED_NAMES: + return NO + + elif prev.type == token.EQUAL: + # A bit hacky: if the equal sign has whitespace, it means we + # previously found it's a typed argument. So, we're using that, too. + return prev.prefix + + elif prev.type != token.COMMA: + return NO + + elif p.type in TYPED_NAMES: + # type names + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type != token.COMMA: + return NO + + elif p.type == syms.trailer: + # attributes and calls + if t == token.LPAR or t == token.RPAR: + return NO + + if not prev: + if t == token.DOT or t == token.LSQB: + return NO + + elif prev.type != token.COMMA: + return NO + + elif p.type == syms.argument: + # single argument + if t == token.EQUAL: + return NO + + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type == token.LPAR: + return NO + + elif prev.type in {token.EQUAL} | VARARGS_SPECIALS: + return NO + + elif p.type == syms.decorator: + # decorators + return NO + + elif p.type == syms.dotted_name: + if prev: + return NO + + prevp = preceding_leaf(p) + if not prevp or prevp.type == token.AT or prevp.type == token.DOT: + return NO + + elif p.type == syms.classdef: + if t == token.LPAR: + return NO + + if prev and prev.type == token.LPAR: + return NO + + elif p.type in {syms.subscript, syms.sliceop}: + # indexing + if not prev: + assert p.parent is not None, "subscripts are always parented" + if p.parent.type == syms.subscriptlist: + return SPACE + + return NO + + elif not complex_subscript: + return NO + + elif p.type == syms.atom: + if prev and t == token.DOT: + # dots, but not the first one. + return NO + + elif p.type == syms.dictsetmaker: + # dict unpacking + if prev and prev.type == token.DOUBLESTAR: + return NO + + elif p.type in {syms.factor, syms.star_expr}: + # unary ops + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + return NO + + prevp_parent = prevp.parent + assert prevp_parent is not None + if prevp.type == token.COLON and prevp_parent.type in { + syms.subscript, + syms.sliceop, + }: + return NO + + elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument: + return NO + + elif t in {token.NAME, token.NUMBER, token.STRING}: + return NO + + elif p.type == syms.import_from: + if t == token.DOT: + if prev and prev.type == token.DOT: + return NO + + elif t == token.NAME: + if v == "import": + return SPACE + + if prev and prev.type == token.DOT: + return NO + + elif p.type == syms.sliceop: + return NO + + elif p.type == syms.except_clause: + if t == token.STAR: + return NO + + return SPACE + + +def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]: + """Return the first leaf that precedes `node`, if any.""" + while node: + res = node.prev_sibling + if res: + if isinstance(res, Leaf): + return res + + try: + return list(res.leaves())[-1] + + except IndexError: + return None + + node = node.parent + return None + + +def prev_siblings_are(node: Optional[LN], tokens: List[Optional[NodeType]]) -> bool: + """Return if the `node` and its previous siblings match types against the provided + list of tokens; the provided `node`has its type matched against the last element in + the list. `None` can be used as the first element to declare that the start of the + list is anchored at the start of its parent's children.""" + if not tokens: + return True + if tokens[-1] is None: + return node is None + if not node: + return False + if node.type != tokens[-1]: + return False + return prev_siblings_are(node.prev_sibling, tokens[:-1]) + + +def parent_type(node: Optional[LN]) -> Optional[NodeType]: + """ + Returns: + @node.parent.type, if @node is not None and has a parent. + OR + None, otherwise. + """ + if node is None or node.parent is None: + return None + + return node.parent.type + + +def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]: + """Return the child of `ancestor` that contains `descendant`.""" + node: Optional[LN] = descendant + while node and node.parent != ancestor: + node = node.parent + return node + + +def replace_child(old_child: LN, new_child: LN) -> None: + """ + Side Effects: + * If @old_child.parent is set, replace @old_child with @new_child in + @old_child's underlying Node structure. + OR + * Otherwise, this function does nothing. + """ + parent = old_child.parent + if not parent: + return + + child_idx = old_child.remove() + if child_idx is not None: + parent.insert_child(child_idx, new_child) + + +def container_of(leaf: Leaf) -> LN: + """Return `leaf` or one of its ancestors that is the topmost container of it. + + By "container" we mean a node where `leaf` is the very first child. + """ + same_prefix = leaf.prefix + container: LN = leaf + while container: + parent = container.parent + if parent is None: + break + + if parent.children[0].prefix != same_prefix: + break + + if parent.type == syms.file_input: + break + + if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS: + break + + container = parent + return container + + +def first_leaf_of(node: LN) -> Optional[Leaf]: + """Returns the first leaf of the node tree.""" + if isinstance(node, Leaf): + return node + if node.children: + return first_leaf_of(node.children[0]) + else: + return None + + +def is_arith_like(node: LN) -> bool: + """Whether node is an arithmetic or a binary arithmetic expression""" + return node.type in { + syms.arith_expr, + syms.shift_expr, + syms.xor_expr, + syms.and_expr, + } + + +def is_docstring(leaf: Leaf) -> bool: + if prev_siblings_are( + leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt] + ): + return True + + # Multiline docstring on the same line as the `def`. + if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]): + # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python + # grammar. We're safe to return True without further checks. + return True + + return False + + +def is_empty_tuple(node: LN) -> bool: + """Return True if `node` holds an empty tuple.""" + return ( + node.type == syms.atom + and len(node.children) == 2 + and node.children[0].type == token.LPAR + and node.children[1].type == token.RPAR + ) + + +def is_one_tuple(node: LN) -> bool: + """Return True if `node` holds a tuple with one element, with or without parens.""" + if node.type == syms.atom: + gexp = unwrap_singleton_parenthesis(node) + if gexp is None or gexp.type != syms.testlist_gexp: + return False + + return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA + + return ( + node.type in IMPLICIT_TUPLE + and len(node.children) == 2 + and node.children[1].type == token.COMMA + ) + + +def is_tuple_containing_walrus(node: LN) -> bool: + """Return True if `node` holds a tuple that contains a walrus operator.""" + if node.type != syms.atom: + return False + gexp = unwrap_singleton_parenthesis(node) + if gexp is None or gexp.type != syms.testlist_gexp: + return False + + return any(child.type == syms.namedexpr_test for child in gexp.children) + + +def is_one_sequence_between( + opening: Leaf, + closing: Leaf, + leaves: List[Leaf], + brackets: Tuple[int, int] = (token.LPAR, token.RPAR), +) -> bool: + """Return True if content between `opening` and `closing` is a one-sequence.""" + if (opening.type, closing.type) != brackets: + return False + + depth = closing.bracket_depth + 1 + for _opening_index, leaf in enumerate(leaves): + if leaf is opening: + break + + else: + raise LookupError("Opening paren not found in `leaves`") + + commas = 0 + _opening_index += 1 + for leaf in leaves[_opening_index:]: + if leaf is closing: + break + + bracket_depth = leaf.bracket_depth + if bracket_depth == depth and leaf.type == token.COMMA: + commas += 1 + if leaf.parent and leaf.parent.type in { + syms.arglist, + syms.typedargslist, + }: + commas += 1 + break + + return commas < 2 + + +def is_walrus_assignment(node: LN) -> bool: + """Return True iff `node` is of the shape ( test := test )""" + inner = unwrap_singleton_parenthesis(node) + return inner is not None and inner.type == syms.namedexpr_test + + +def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool: + """Return True iff `node` is a trailer valid in a simple decorator""" + return node.type == syms.trailer and ( + ( + len(node.children) == 2 + and node.children[0].type == token.DOT + and node.children[1].type == token.NAME + ) + # last trailer can be an argument-less parentheses pair + or ( + last + and len(node.children) == 2 + and node.children[0].type == token.LPAR + and node.children[1].type == token.RPAR + ) + # last trailer can be arguments + or ( + last + and len(node.children) == 3 + and node.children[0].type == token.LPAR + # and node.children[1].type == syms.argument + and node.children[2].type == token.RPAR + ) + ) + + +def is_simple_decorator_expression(node: LN) -> bool: + """Return True iff `node` could be a 'dotted name' decorator + + This function takes the node of the 'namedexpr_test' of the new decorator + grammar and test if it would be valid under the old decorator grammar. + + The old grammar was: decorator: @ dotted_name [arguments] NEWLINE + The new grammar is : decorator: @ namedexpr_test NEWLINE + """ + if node.type == token.NAME: + return True + if node.type == syms.power: + if node.children: + return ( + node.children[0].type == token.NAME + and all(map(is_simple_decorator_trailer, node.children[1:-1])) + and ( + len(node.children) < 2 + or is_simple_decorator_trailer(node.children[-1], last=True) + ) + ) + return False + + +def is_yield(node: LN) -> bool: + """Return True if `node` holds a `yield` or `yield from` expression.""" + if node.type == syms.yield_expr: + return True + + if is_name_token(node) and node.value == "yield": + return True + + if node.type != syms.atom: + return False + + if len(node.children) != 3: + return False + + lpar, expr, rpar = node.children + if lpar.type == token.LPAR and rpar.type == token.RPAR: + return is_yield(expr) + + return False + + +def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool: + """Return True if `leaf` is a star or double star in a vararg or kwarg. + + If `within` includes VARARGS_PARENTS, this applies to function signatures. + If `within` includes UNPACKING_PARENTS, it applies to right hand-side + extended iterable unpacking (PEP 3132) and additional unpacking + generalizations (PEP 448). + """ + if leaf.type not in VARARGS_SPECIALS or not leaf.parent: + return False + + p = leaf.parent + if p.type == syms.star_expr: + # Star expressions are also used as assignment targets in extended + # iterable unpacking (PEP 3132). See what its parent is instead. + if not p.parent: + return False + + p = p.parent + + return p.type in within + + +def is_multiline_string(leaf: Leaf) -> bool: + """Return True if `leaf` is a multiline string that actually spans many lines.""" + return has_triple_quotes(leaf.value) and "\n" in leaf.value + + +def is_stub_suite(node: Node) -> bool: + """Return True if `node` is a suite with a stub body.""" + if ( + len(node.children) != 4 + or node.children[0].type != token.NEWLINE + or node.children[1].type != token.INDENT + or node.children[3].type != token.DEDENT + ): + return False + + return is_stub_body(node.children[2]) + + +def is_stub_body(node: LN) -> bool: + """Return True if `node` is a simple statement containing an ellipsis.""" + if not isinstance(node, Node) or node.type != syms.simple_stmt: + return False + + if len(node.children) != 2: + return False + + child = node.children[0] + return ( + child.type == syms.atom + and len(child.children) == 3 + and all(leaf == Leaf(token.DOT, ".") for leaf in child.children) + ) + + +def is_atom_with_invisible_parens(node: LN) -> bool: + """Given a `LN`, determines whether it's an atom `node` with invisible + parens. Useful in dedupe-ing and normalizing parens. + """ + if isinstance(node, Leaf) or node.type != syms.atom: + return False + + first, last = node.children[0], node.children[-1] + return ( + isinstance(first, Leaf) + and first.type == token.LPAR + and first.value == "" + and isinstance(last, Leaf) + and last.type == token.RPAR + and last.value == "" + ) + + +def is_empty_par(leaf: Leaf) -> bool: + return is_empty_lpar(leaf) or is_empty_rpar(leaf) + + +def is_empty_lpar(leaf: Leaf) -> bool: + return leaf.type == token.LPAR and leaf.value == "" + + +def is_empty_rpar(leaf: Leaf) -> bool: + return leaf.type == token.RPAR and leaf.value == "" + + +def is_import(leaf: Leaf) -> bool: + """Return True if the given leaf starts an import statement.""" + p = leaf.parent + t = leaf.type + v = leaf.value + return bool( + t == token.NAME + and ( + (v == "import" and p and p.type == syms.import_name) + or (v == "from" and p and p.type == syms.import_from) + ) + ) + + +def is_type_comment(leaf: Leaf, suffix: str = "") -> bool: + """Return True if the given leaf is a special comment. + Only returns true for type comments for now.""" + t = leaf.type + v = leaf.value + return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix) + + +def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None: + """Wrap `child` in parentheses. + + This replaces `child` with an atom holding the parentheses and the old + child. That requires moving the prefix. + + If `visible` is False, the leaves will be valueless (and thus invisible). + """ + lpar = Leaf(token.LPAR, "(" if visible else "") + rpar = Leaf(token.RPAR, ")" if visible else "") + prefix = child.prefix + child.prefix = "" + index = child.remove() or 0 + new_child = Node(syms.atom, [lpar, child, rpar]) + new_child.prefix = prefix + parent.insert_child(index, new_child) + + +def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]: + """Returns `wrapped` if `node` is of the shape ( wrapped ). + + Parenthesis can be optional. Returns None otherwise""" + if len(node.children) != 3: + return None + + lpar, wrapped, rpar = node.children + if not (lpar.type == token.LPAR and rpar.type == token.RPAR): + return None + + return wrapped + + +def ensure_visible(leaf: Leaf) -> None: + """Make sure parentheses are visible. + + They could be invisible as part of some statements (see + :func:`normalize_invisible_parens` and :func:`visit_import_from`). + """ + if leaf.type == token.LPAR: + leaf.value = "(" + elif leaf.type == token.RPAR: + leaf.value = ")" + + +def is_name_token(nl: NL) -> TypeGuard[Leaf]: + return nl.type == token.NAME + + +def is_lpar_token(nl: NL) -> TypeGuard[Leaf]: + return nl.type == token.LPAR + + +def is_rpar_token(nl: NL) -> TypeGuard[Leaf]: + return nl.type == token.RPAR + + +def is_string_token(nl: NL) -> TypeGuard[Leaf]: + return nl.type == token.STRING + + +def is_number_token(nl: NL) -> TypeGuard[Leaf]: + return nl.type == token.NUMBER + + +def is_part_of_annotation(leaf: Leaf) -> bool: + """Returns whether this leaf is part of type annotations.""" + ancestor = leaf.parent + while ancestor is not None: + if ancestor.prev_sibling and ancestor.prev_sibling.type == token.RARROW: + return True + if ancestor.parent and ancestor.parent.type == syms.tname: + return True + ancestor = ancestor.parent + return False diff --git a/.venv/lib/python3.11/site-packages/black/numerics.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/numerics.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..3e292c0ca17f2b80c66a1875d5c137344a830bba GIT binary patch literal 8256 zcmeHMYit}>6~61WlWmi%m(VoEiJB3lV54U2tRHm=rt9}O*sk$|M}-;B?u_lF`!Kt+ zsT~SfN*{5nNKqsZLP9b{6x1Imsze0^AvY;)gaDZWA_$NoR|Y$c)Raf$N^O^O?wqrp zon5yTKlnqgHFNIy&f`A!&bh~5>lx^+@c9H6zxaeeZfS>uL`V?%09AlQM4Q+N?}Ori z)UDbQmDJu0I1p1I6bWD(*u>@@$pTemg{fBI;KM}O4q3FT2|Dtcpdu?wmuV$zSY!mAyIr380XUcY;0y~;Q>F3>EAyOEj_O_ds0L=$e8jc*> zZQ#QXPv6kESiSb@-@=dA{`k3H?fGGT^k`I@v?j3$l-~jm&;RM=N#8MsKgiQ7daq!dZlB)(s09{f*)d-bu8dtrtZ z&*I-M@!euKZ%E*=_xh(`!N>%oPtPbbqUJ!=~oTi1miPsMGaPXQ5n z|Ja~zS{Z9Pp0%ya*kD&8owCM^Xu@)2X*hSbBeQHl49iK~{(#Oq(DS`tZEoZ5fF~IwlhFXiUwf z)mEWfrePZ}u4p#v3=5!?Df$NbJG=C-8dh6qPPRw|>|ggE?q{F41PZ)Ian@zTg8G{-ocn}$ z4edYFEgXqZS6n#$wz<@~aCd(iUAX()Zgb)8cC}O20@@Q*Ke|N`+9S($)%r1LK|A$A z;G)w~zU2al@^`)f@9IMls3AXt!i_6=D0e=GJWkcdisYX`9*3~8EcvIA$F6Ujm;95+ zW0yCUfY;7cEdijNX+H;KT&v&I7T2^>o52SwCN4t9ZbKn->>m^PE7(5h#~uBsOaJ*z zzqWWmd;aRZnr}t>?Pg9I?|UtP#xH}hw)kRx73~+=>%mUsKBAp!|09T@$;CB$H?+UH zLlNr}(53Ymcul-mB|y>l&x>d^rB)w$7`iNAYTD0(qOd;_b{UkPB#wFRgzEku&XI2I)$20 z|Ni`yqs#Y0Hx@u3(7qjLLB3^gIQF=JU?20?11i@Vm$o(9bkE(U+1hM1Hbz^Fsi|<2 zsq4x4+4-1w2bqdzkseJLu?OI+D4Y($cffy40spwje|amP|2WXQVAv+m1ki_o;Z-d)G)^;=P9V+*r}0%pIcEG$_QBM{jCwFw=JUh`f%} zdpqUf{TTD}1iNH@h>&?7xnPJt{DSvSF-EWGR1i5z0&csN9^N`>oV|*U?HF3f}!1Nak{L2sF0s2)_)(w zV~QJ+bwAu_17-2|3qD`mMt+F?%I!nH*eCeDRjd!UY5H9kEy3b@P$@UA?Z*3S>8Dz3de;5V5--1w<^2C$>X)wXza(C|zHb2zo2O(S zhM=RqGI^MTgnQRL47ehF6~Bf6FPG05mw2%*at7dgMd|NodK>*Yz?D)r;<0V?zb*07 zJkpbZm&^Y=L;Ac=xT&9${#p7!alYs`fS2pf>NfbBfXj;$J0@BKAIft1D-p}sSvxm1 zrN$stbG&OrALt()gIv&7zDc)}dJHp8Sa+oM?=-O+RpVYeO^_ zPnZqzM%uvlQ8|F#k&eM0y=SN!Q)qHHTsO06ea1+cn6K*o+)&40e-~)L2Tt$NbjM8V z9svl_YwVzJ;J(g|0sX$--qD^heXOH%paqDW( z5vn<#0!xS58An3#K@p>aEMp}MG@#mS!WJ^)AXM9$gSyOts+qJ*C#u#A?Ufl5Y?*T` zoc-lkaQ%xhP(xE}2fWBW8OeAI`jH0j@KR-ykfszWY~>_uPtT9%zhLqEfj?!APdT1p zdf4NW!p8!N-+x2MhMqU9!Nq?IYxf>7-j3 z>YN{E`c;qp64^72dlroC*zLm}d;b3hQ@$s$zPJC6lYN*JI9_Es#D+w!R>S09e5Xe_V<#)c$G8N8mYd7d+e_K7aA|#eH|%*WqCU~pwYyTgs-)0s7 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/numerics.py b/.venv/lib/python3.11/site-packages/black/numerics.py new file mode 100644 index 00000000..879e5b2c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/numerics.py @@ -0,0 +1,60 @@ +""" +Formatting numeric literals. +""" +from blib2to3.pytree import Leaf + + +def format_hex(text: str) -> str: + """ + Formats a hexadecimal string like "0x12B3" + """ + before, after = text[:2], text[2:] + return f"{before}{after.upper()}" + + +def format_scientific_notation(text: str) -> str: + """Formats a numeric string utilizing scentific notation""" + before, after = text.split("e") + sign = "" + if after.startswith("-"): + after = after[1:] + sign = "-" + elif after.startswith("+"): + after = after[1:] + before = format_float_or_int_string(before) + return f"{before}e{sign}{after}" + + +def format_complex_number(text: str) -> str: + """Formats a complex string like `10j`""" + number = text[:-1] + suffix = text[-1] + return f"{format_float_or_int_string(number)}{suffix}" + + +def format_float_or_int_string(text: str) -> str: + """Formats a float string like "1.0".""" + if "." not in text: + return text + + before, after = text.split(".") + return f"{before or 0}.{after or 0}" + + +def normalize_numeric_literal(leaf: Leaf) -> None: + """Normalizes numeric (float, int, and complex) literals. + + All letters used in the representation are normalized to lowercase.""" + text = leaf.value.lower() + if text.startswith(("0o", "0b")): + # Leave octal and binary literals alone. + pass + elif text.startswith("0x"): + text = format_hex(text) + elif "e" in text: + text = format_scientific_notation(text) + elif text.endswith("j"): + text = format_complex_number(text) + else: + text = format_float_or_int_string(text) + leaf.value = text diff --git a/.venv/lib/python3.11/site-packages/black/output.py b/.venv/lib/python3.11/site-packages/black/output.py new file mode 100644 index 00000000..f4c17f28 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/output.py @@ -0,0 +1,105 @@ +"""Nice output for Black. + +The double calls are for patching purposes in tests. +""" + +import json +import tempfile +from typing import Any, Optional + +from click import echo, style +from mypy_extensions import mypyc_attr + + +@mypyc_attr(patchable=True) +def _out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + if message is not None: + if "bold" not in styles: + styles["bold"] = True + message = style(message, **styles) + echo(message, nl=nl, err=True) + + +@mypyc_attr(patchable=True) +def _err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + if message is not None: + if "fg" not in styles: + styles["fg"] = "red" + message = style(message, **styles) + echo(message, nl=nl, err=True) + + +@mypyc_attr(patchable=True) +def out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + _out(message, nl=nl, **styles) + + +def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + _err(message, nl=nl, **styles) + + +def ipynb_diff(a: str, b: str, a_name: str, b_name: str) -> str: + """Return a unified diff string between each cell in notebooks `a` and `b`.""" + a_nb = json.loads(a) + b_nb = json.loads(b) + diff_lines = [ + diff( + "".join(a_nb["cells"][cell_number]["source"]) + "\n", + "".join(b_nb["cells"][cell_number]["source"]) + "\n", + f"{a_name}:cell_{cell_number}", + f"{b_name}:cell_{cell_number}", + ) + for cell_number, cell in enumerate(a_nb["cells"]) + if cell["cell_type"] == "code" + ] + return "".join(diff_lines) + + +def diff(a: str, b: str, a_name: str, b_name: str) -> str: + """Return a unified diff string between strings `a` and `b`.""" + import difflib + + a_lines = a.splitlines(keepends=True) + b_lines = b.splitlines(keepends=True) + diff_lines = [] + for line in difflib.unified_diff( + a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5 + ): + # Work around https://bugs.python.org/issue2142 + # See: + # https://www.gnu.org/software/diffutils/manual/html_node/Incomplete-Lines.html + if line[-1] == "\n": + diff_lines.append(line) + else: + diff_lines.append(line + "\n") + diff_lines.append("\\ No newline at end of file\n") + return "".join(diff_lines) + + +def color_diff(contents: str) -> str: + """Inject the ANSI color codes to the diff.""" + lines = contents.split("\n") + for i, line in enumerate(lines): + if line.startswith("+++") or line.startswith("---"): + line = "\033[1m" + line + "\033[0m" # bold, reset + elif line.startswith("@@"): + line = "\033[36m" + line + "\033[0m" # cyan, reset + elif line.startswith("+"): + line = "\033[32m" + line + "\033[0m" # green, reset + elif line.startswith("-"): + line = "\033[31m" + line + "\033[0m" # red, reset + lines[i] = line + return "\n".join(lines) + + +@mypyc_attr(patchable=True) +def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str: + """Dump `output` to a temporary file. Return path to the file.""" + with tempfile.NamedTemporaryFile( + mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8" + ) as f: + for lines in output: + f.write(lines) + if ensure_final_newline and lines and lines[-1] != "\n": + f.write("\n") + return f.name diff --git a/.venv/lib/python3.11/site-packages/black/parsing.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/parsing.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..57cd2094388247e9fe8255c96949e6d3c56afc91 GIT binary patch literal 8256 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 INYrirADJ>@W&i*H literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/parsing.py b/.venv/lib/python3.11/site-packages/black/parsing.py new file mode 100644 index 00000000..ba474c5e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/parsing.py @@ -0,0 +1,277 @@ +""" +Parse Python code and perform AST validation. +""" +import ast +import platform +import sys +from typing import Any, Iterable, Iterator, List, Set, Tuple, Type, Union + +if sys.version_info < (3, 8): + from typing_extensions import Final +else: + from typing import Final + +from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature +from black.nodes import syms +from blib2to3 import pygram +from blib2to3.pgen2 import driver +from blib2to3.pgen2.grammar import Grammar +from blib2to3.pgen2.parse import ParseError +from blib2to3.pgen2.tokenize import TokenError +from blib2to3.pytree import Leaf, Node + +ast3: Any + +_IS_PYPY = platform.python_implementation() == "PyPy" + +try: + from typed_ast import ast3 +except ImportError: + if sys.version_info < (3, 8) and not _IS_PYPY: + print( + ( + "The typed_ast package is required but not installed.\n" + "You can upgrade to Python 3.8+ or install typed_ast with\n" + "`python3 -m pip install typed-ast`." + ), + file=sys.stderr, + ) + sys.exit(1) + else: + ast3 = ast + + +PY2_HINT: Final = "Python 2 support was removed in version 22.0." + + +class InvalidInput(ValueError): + """Raised when input source code fails all parse attempts.""" + + +def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: + if not target_versions: + # No target_version specified, so try all grammars. + return [ + # Python 3.7-3.9 + pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords, + # Python 3.0-3.6 + pygram.python_grammar_no_print_statement_no_exec_statement, + # Python 3.10+ + pygram.python_grammar_soft_keywords, + ] + + grammars = [] + # If we have to parse both, try to parse async as a keyword first + if not supports_feature( + target_versions, Feature.ASYNC_IDENTIFIERS + ) and not supports_feature(target_versions, Feature.PATTERN_MATCHING): + # Python 3.7-3.9 + grammars.append( + pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords + ) + if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS): + # Python 3.0-3.6 + grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement) + if any(Feature.PATTERN_MATCHING in VERSION_TO_FEATURES[v] for v in target_versions): + # Python 3.10+ + grammars.append(pygram.python_grammar_soft_keywords) + + # At least one of the above branches must have been taken, because every Python + # version has exactly one of the two 'ASYNC_*' flags + return grammars + + +def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node: + """Given a string with source, return the lib2to3 Node.""" + if not src_txt.endswith("\n"): + src_txt += "\n" + + grammars = get_grammars(set(target_versions)) + errors = {} + for grammar in grammars: + drv = driver.Driver(grammar) + try: + result = drv.parse_string(src_txt, True) + break + + except ParseError as pe: + lineno, column = pe.context[1] + lines = src_txt.splitlines() + try: + faulty_line = lines[lineno - 1] + except IndexError: + faulty_line = "" + errors[grammar.version] = InvalidInput( + f"Cannot parse: {lineno}:{column}: {faulty_line}" + ) + + except TokenError as te: + # In edge cases these are raised; and typically don't have a "faulty_line". + lineno, column = te.args[1] + errors[grammar.version] = InvalidInput( + f"Cannot parse: {lineno}:{column}: {te.args[0]}" + ) + + else: + # Choose the latest version when raising the actual parsing error. + assert len(errors) >= 1 + exc = errors[max(errors)] + + if matches_grammar(src_txt, pygram.python_grammar) or matches_grammar( + src_txt, pygram.python_grammar_no_print_statement + ): + original_msg = exc.args[0] + msg = f"{original_msg}\n{PY2_HINT}" + raise InvalidInput(msg) from None + + raise exc from None + + if isinstance(result, Leaf): + result = Node(syms.file_input, [result]) + return result + + +def matches_grammar(src_txt: str, grammar: Grammar) -> bool: + drv = driver.Driver(grammar) + try: + drv.parse_string(src_txt, True) + except (ParseError, TokenError, IndentationError): + return False + else: + return True + + +def lib2to3_unparse(node: Node) -> str: + """Given a lib2to3 node, return its string representation.""" + code = str(node) + return code + + +def parse_single_version( + src: str, version: Tuple[int, int] +) -> Union[ast.AST, ast3.AST]: + filename = "" + # typed-ast is needed because of feature version limitations in the builtin ast 3.8> + if sys.version_info >= (3, 8) and version >= (3,): + return ast.parse(src, filename, feature_version=version, type_comments=True) + + if _IS_PYPY: + # PyPy 3.7 doesn't support type comment tracking which is not ideal, but there's + # not much we can do as typed-ast won't work either. + if sys.version_info >= (3, 8): + return ast3.parse(src, filename, type_comments=True) + else: + return ast3.parse(src, filename) + else: + # Typed-ast is guaranteed to be used here and automatically tracks type + # comments separately. + return ast3.parse(src, filename, feature_version=version[1]) + + +def parse_ast(src: str) -> Union[ast.AST, ast3.AST]: + # TODO: support Python 4+ ;) + versions = [(3, minor) for minor in range(3, sys.version_info[1] + 1)] + + first_error = "" + for version in sorted(versions, reverse=True): + try: + return parse_single_version(src, version) + except SyntaxError as e: + if not first_error: + first_error = str(e) + + raise SyntaxError(first_error) + + +ast3_AST: Final[Type[ast3.AST]] = ast3.AST + + +def _normalize(lineend: str, value: str) -> str: + # To normalize, we strip any leading and trailing space from + # each line... + stripped: List[str] = [i.strip() for i in value.splitlines()] + normalized = lineend.join(stripped) + # ...and remove any blank lines at the beginning and end of + # the whole string + return normalized.strip() + + +def stringify_ast(node: Union[ast.AST, ast3.AST], depth: int = 0) -> Iterator[str]: + """Simple visitor generating strings to compare ASTs by content.""" + + node = fixup_ast_constants(node) + + yield f"{' ' * depth}{node.__class__.__name__}(" + + type_ignore_classes: Tuple[Type[Any], ...] + for field in sorted(node._fields): # noqa: F402 + # TypeIgnore will not be present using pypy < 3.8, so need for this + if not (_IS_PYPY and sys.version_info < (3, 8)): + # TypeIgnore has only one field 'lineno' which breaks this comparison + type_ignore_classes = (ast3.TypeIgnore,) + if sys.version_info >= (3, 8): + type_ignore_classes += (ast.TypeIgnore,) + if isinstance(node, type_ignore_classes): + break + + try: + value: object = getattr(node, field) + except AttributeError: + continue + + yield f"{' ' * (depth+1)}{field}=" + + if isinstance(value, list): + for item in value: + # Ignore nested tuples within del statements, because we may insert + # parentheses and they change the AST. + if ( + field == "targets" + and isinstance(node, (ast.Delete, ast3.Delete)) + and isinstance(item, (ast.Tuple, ast3.Tuple)) + ): + for elt in item.elts: + yield from stringify_ast(elt, depth + 2) + + elif isinstance(item, (ast.AST, ast3.AST)): + yield from stringify_ast(item, depth + 2) + + # Note that we are referencing the typed-ast ASTs via global variables and not + # direct module attribute accesses because that breaks mypyc. It's probably + # something to do with the ast3 variables being marked as Any leading + # mypy to think this branch is always taken, leaving the rest of the code + # unanalyzed. Tighting up the types for the typed-ast AST types avoids the + # mypyc crash. + elif isinstance(value, (ast.AST, ast3_AST)): + yield from stringify_ast(value, depth + 2) + + else: + normalized: object + # Constant strings may be indented across newlines, if they are + # docstrings; fold spaces after newlines when comparing. Similarly, + # trailing and leading space may be removed. + if ( + isinstance(node, ast.Constant) + and field == "value" + and isinstance(value, str) + ): + normalized = _normalize("\n", value) + else: + normalized = value + yield f"{' ' * (depth+2)}{normalized!r}, # {value.__class__.__name__}" + + yield f"{' ' * depth}) # /{node.__class__.__name__}" + + +def fixup_ast_constants(node: Union[ast.AST, ast3.AST]) -> Union[ast.AST, ast3.AST]: + """Map ast nodes deprecated in 3.8 to Constant.""" + if isinstance(node, (ast.Str, ast3.Str, ast.Bytes, ast3.Bytes)): + return ast.Constant(value=node.s) + + if isinstance(node, (ast.Num, ast3.Num)): + return ast.Constant(value=node.n) + + if isinstance(node, (ast.NameConstant, ast3.NameConstant)): + return ast.Constant(value=node.value) + + return node diff --git a/.venv/lib/python3.11/site-packages/black/py.typed b/.venv/lib/python3.11/site-packages/black/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/black/report.py b/.venv/lib/python3.11/site-packages/black/report.py new file mode 100644 index 00000000..a507671e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/report.py @@ -0,0 +1,106 @@ +""" +Summarize Black runs to users. +""" +from dataclasses import dataclass +from enum import Enum +from pathlib import Path + +from click import style + +from black.output import err, out + + +class Changed(Enum): + NO = 0 + CACHED = 1 + YES = 2 + + +class NothingChanged(UserWarning): + """Raised when reformatted code is the same as source.""" + + +@dataclass +class Report: + """Provides a reformatting counter. Can be rendered with `str(report)`.""" + + check: bool = False + diff: bool = False + quiet: bool = False + verbose: bool = False + change_count: int = 0 + same_count: int = 0 + failure_count: int = 0 + + def done(self, src: Path, changed: Changed) -> None: + """Increment the counter for successful reformatting. Write out a message.""" + if changed is Changed.YES: + reformatted = "would reformat" if self.check or self.diff else "reformatted" + if self.verbose or not self.quiet: + out(f"{reformatted} {src}") + self.change_count += 1 + else: + if self.verbose: + if changed is Changed.NO: + msg = f"{src} already well formatted, good job." + else: + msg = f"{src} wasn't modified on disk since last run." + out(msg, bold=False) + self.same_count += 1 + + def failed(self, src: Path, message: str) -> None: + """Increment the counter for failed reformatting. Write out a message.""" + err(f"error: cannot format {src}: {message}") + self.failure_count += 1 + + def path_ignored(self, path: Path, message: str) -> None: + if self.verbose: + out(f"{path} ignored: {message}", bold=False) + + @property + def return_code(self) -> int: + """Return the exit code that the app should use. + + This considers the current state of changed files and failures: + - if there were any failures, return 123; + - if any files were changed and --check is being used, return 1; + - otherwise return 0. + """ + # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with + # 126 we have special return codes reserved by the shell. + if self.failure_count: + return 123 + + elif self.change_count and self.check: + return 1 + + return 0 + + def __str__(self) -> str: + """Render a color report of the current state. + + Use `click.unstyle` to remove colors. + """ + if self.check or self.diff: + reformatted = "would be reformatted" + unchanged = "would be left unchanged" + failed = "would fail to reformat" + else: + reformatted = "reformatted" + unchanged = "left unchanged" + failed = "failed to reformat" + report = [] + if self.change_count: + s = "s" if self.change_count > 1 else "" + report.append( + style(f"{self.change_count} file{s} ", bold=True, fg="blue") + + style(f"{reformatted}", bold=True) + ) + + if self.same_count: + s = "s" if self.same_count > 1 else "" + report.append(style(f"{self.same_count} file{s} ", fg="blue") + unchanged) + if self.failure_count: + s = "s" if self.failure_count > 1 else "" + report.append(style(f"{self.failure_count} file{s} {failed}", fg="red")) + return ", ".join(report) + "." diff --git a/.venv/lib/python3.11/site-packages/black/rusty.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/rusty.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..27703dab97f8f311fd12fa47b670bffd886b5de2 GIT binary patch literal 8256 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 None: + self._value = value + + def ok(self) -> T: + return self._value + + +class Err(Generic[E]): + def __init__(self, e: E) -> None: + self._e = e + + def err(self) -> E: + return self._e + + +Result = Union[Ok[T], Err[E]] diff --git a/.venv/lib/python3.11/site-packages/black/strings.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/strings.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..aea34c69e3adcf29fb0c8361f52627ff1dee10f2 GIT binary patch literal 8256 zcmeHMYit}>6~61W<4x_ZO=%KiN6H9Nuu(I9yDq_W{T>H9bsdLCRWljytnH=yFuSv< z9STHB8)8tTC<27~%Ycfi^iM#d5>!Ilq-u)P9}dr`0%4^D*`-obqE-$HSQ-!uifXq4F2qy_O#3J1 z!sk_zt8}kyMF^&-i)H+pV`JT>hE&S>Oxf-cu%juI?R?NrEh$WpzpdsaK=Z+rh9k#z zFSOx@XZYY@F|%*y^S{1*!}r(c${(r!b~uWRl$SH z(Kq;qw_beX?2}i%_NCpwJpE4a+;3{MH-Gnb@MqUv7zT04vKvsXgycoQcx+zr_qM>X zUz%5u>dyi#)s6+YMmk>7y#+o2_{Y79RKE8^0XW*3hw3CV^W>MU4&8IW5h&d9UPo^A{%L?&hNgIU19A7jCB33Mx42xKFI;^LY zdY3RGw&hqbtm$;x9Th+cTMUnm^!1xevK1v1&r@j{9?N%jHk0s#oB)GV4Qe4 zaMAUY>%0J>+@=T9I8mrK{2YVdz` znx%Rk#U>{cO0hMcwOWQTw{@??)-PI9kX_@Y{W@g&j zZPSb|&M$_=`^a=Wi_Gbm6@DDf3O*g+J!$ao*Pwk|mB%^S55SX! zVac<#Hzbof)_@1OJyZ!0`{qJ3I*-_i$`;i3HU|Ij*H^TRv{( z>mkQk4Wu6+T2GYYEj-h)u>9TIxg_$}$uY+!=P9V+*r|nrpSw^Q%DXO+cNeVI5l?0i z5NZU z{iOE(*M#rQs^2frgwGH?O7tYr9}pex@BfU}2sh3qtwZnDJGHi!*3Oo$wsx&?B4TTX z<+%91j*cb~j#-IW?Pw&GjwTaYyWXLAfz*cNN{=QSQ&53Y*&aUY3h@a+`=- z!*kA8qW^flmBA^qtgH*3bBM1fxK6lwzG95(6Px|y^Wl)hi|6wgwZrE%u(F(lUmx6d z`Mf1qJPVall2I+kkx?^3^beQ!v-cztgH4x6WFAJ#xeeI@cR z4TP)f-U|3VF#l)hz~;P42yjjMDExU0@KSl6u*3^>kxK$zGC#A0{z<^K;%3D6x6prH z;>G!;^MK>HR+MpNNk63Q|JML7<#%-p{9k~}i<8pVo$#S7mA?|JX*_u=kYn(67HUT{DH3K=}a_cH_IDo zGu=mJ|A!`e#|F*8g9DgBlcV9folcr_R>H=7)xehz_KuD8g9d!x%t6C+&5VHwfFQfZ z4GfPy+}As5K0GvZXmHY;?Cl#JEXt9+Z*GoKV4nP#o6svttSQOAE%I!bK2uY61^KlS zDL&c150M`Xg}jM5H~T2`^kUqxrhz&sm(Fo5k#r*ZY$Bt}rPvp>h2-X}^qkP`#ROQo z)JeG#iVg}G6J)7K%t8aI&Bq)e^9@3GA`4KL8Bjfyl-Wc*GDkaQ&IViNTnl%1xfWdi zY8cee6#YOevQJh#8isx(p>_DtrAx?C3LQ3b95$!&>ikzMen0RP=lGQ48K!%cRw;bU zqxhY94`NBS=eUPyh%`CQRE~N0PmY`SXM2v1m>!370t@=ZD%*418v+d5W_ymKm~wm$ zDzZ>>%nAc)7%Q_q$6HJ_(&YZL9@E1h$Jm>Bj?0*`z1n|jR@hKM8QXLG#xzTc>|Y&! zg6#W9kK;V1eBVHO%=N4GD91VB@PY4FO!N09(7}Rz^_Da+Ui%O=#Po<)8S0!LXZlUW zewpl&NY#gGvw{-X9!~@&E5sedd|I0Jb$A`+Pi&jY45Pm)qnSzW@T93--@( zci;o;xxGt@J;&#@AJYE!V2@jd{c{}8|DVm$Lh-zFzj+`1Me)z^{uP`QD2*=3|FS*P zt03`?KOY~GgHYbmt*{-_e*lbi?mx#>A+l#ZZidIf{B@|H{}u*_vi(;SoTfwCpa^;1 zyI$30@_&l*5qJ*V4-fZ;&tH7MxbI&3Mm%hw>{c3Q|E^RBxN(%UA0P)eS&^t}{~wzS BWE21Z literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/strings.py b/.venv/lib/python3.11/site-packages/black/strings.py new file mode 100644 index 00000000..3e3bc12f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/strings.py @@ -0,0 +1,280 @@ +""" +Simple formatting on strings. Further string formatting code is in trans.py. +""" + +import re +import sys +from functools import lru_cache +from typing import List, Match, Pattern + +from blib2to3.pytree import Leaf + +if sys.version_info < (3, 8): + from typing_extensions import Final +else: + from typing import Final + + +STRING_PREFIX_CHARS: Final = "furbFURB" # All possible string prefix characters. +STRING_PREFIX_RE: Final = re.compile( + r"^([" + STRING_PREFIX_CHARS + r"]*)(.*)$", re.DOTALL +) +FIRST_NON_WHITESPACE_RE: Final = re.compile(r"\s*\t+\s*(\S)") +UNICODE_ESCAPE_RE: Final = re.compile( + r"(?P\\+)(?P" + r"(u(?P[a-fA-F0-9]{4}))" # Character with 16-bit hex value xxxx + r"|(U(?P[a-fA-F0-9]{8}))" # Character with 32-bit hex value xxxxxxxx + r"|(x(?P[a-fA-F0-9]{2}))" # Character with hex value hh + r"|(N\{(?P[a-zA-Z0-9 \-]{2,})\})" # Character named name in the Unicode database + r")", + re.VERBOSE, +) + + +def sub_twice(regex: Pattern[str], replacement: str, original: str) -> str: + """Replace `regex` with `replacement` twice on `original`. + + This is used by string normalization to perform replaces on + overlapping matches. + """ + return regex.sub(replacement, regex.sub(replacement, original)) + + +def has_triple_quotes(string: str) -> bool: + """ + Returns: + True iff @string starts with three quotation characters. + """ + raw_string = string.lstrip(STRING_PREFIX_CHARS) + return raw_string[:3] in {'"""', "'''"} + + +def lines_with_leading_tabs_expanded(s: str) -> List[str]: + """ + Splits string into lines and expands only leading tabs (following the normal + Python rules) + """ + lines = [] + for line in s.splitlines(): + # Find the index of the first non-whitespace character after a string of + # whitespace that includes at least one tab + match = FIRST_NON_WHITESPACE_RE.match(line) + if match: + first_non_whitespace_idx = match.start(1) + + lines.append( + line[:first_non_whitespace_idx].expandtabs() + + line[first_non_whitespace_idx:] + ) + else: + lines.append(line) + return lines + + +def fix_docstring(docstring: str, prefix: str) -> str: + # https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation + if not docstring: + return "" + lines = lines_with_leading_tabs_expanded(docstring) + # Determine minimum indentation (first line doesn't count): + indent = sys.maxsize + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < sys.maxsize: + last_line_idx = len(lines) - 2 + for i, line in enumerate(lines[1:]): + stripped_line = line[indent:].rstrip() + if stripped_line or i == last_line_idx: + trimmed.append(prefix + stripped_line) + else: + trimmed.append("") + return "\n".join(trimmed) + + +def get_string_prefix(string: str) -> str: + """ + Pre-conditions: + * assert_is_leaf_string(@string) + + Returns: + @string's prefix (e.g. '', 'r', 'f', or 'rf'). + """ + assert_is_leaf_string(string) + + prefix = "" + prefix_idx = 0 + while string[prefix_idx] in STRING_PREFIX_CHARS: + prefix += string[prefix_idx] + prefix_idx += 1 + + return prefix + + +def assert_is_leaf_string(string: str) -> None: + """ + Checks the pre-condition that @string has the format that you would expect + of `leaf.value` where `leaf` is some Leaf such that `leaf.type == + token.STRING`. A more precise description of the pre-conditions that are + checked are listed below. + + Pre-conditions: + * @string starts with either ', ", ', or " where + `set()` is some subset of `set(STRING_PREFIX_CHARS)`. + * @string ends with a quote character (' or "). + + Raises: + AssertionError(...) if the pre-conditions listed above are not + satisfied. + """ + dquote_idx = string.find('"') + squote_idx = string.find("'") + if -1 in [dquote_idx, squote_idx]: + quote_idx = max(dquote_idx, squote_idx) + else: + quote_idx = min(squote_idx, dquote_idx) + + assert ( + 0 <= quote_idx < len(string) - 1 + ), f"{string!r} is missing a starting quote character (' or \")." + assert string[-1] in ( + "'", + '"', + ), f"{string!r} is missing an ending quote character (' or \")." + assert set(string[:quote_idx]).issubset( + set(STRING_PREFIX_CHARS) + ), f"{set(string[:quote_idx])} is NOT a subset of {set(STRING_PREFIX_CHARS)}." + + +def normalize_string_prefix(s: str) -> str: + """Make all string prefixes lowercase.""" + match = STRING_PREFIX_RE.match(s) + assert match is not None, f"failed to match string {s!r}" + orig_prefix = match.group(1) + new_prefix = ( + orig_prefix.replace("F", "f") + .replace("B", "b") + .replace("U", "") + .replace("u", "") + ) + + # Python syntax guarantees max 2 prefixes and that one of them is "r" + if len(new_prefix) == 2 and "r" != new_prefix[0].lower(): + new_prefix = new_prefix[::-1] + return f"{new_prefix}{match.group(2)}" + + +# Re(gex) does actually cache patterns internally but this still improves +# performance on a long list literal of strings by 5-9% since lru_cache's +# caching overhead is much lower. +@lru_cache(maxsize=64) +def _cached_compile(pattern: str) -> Pattern[str]: + return re.compile(pattern) + + +def normalize_string_quotes(s: str) -> str: + """Prefer double quotes but only if it doesn't cause more escaping. + + Adds or removes backslashes as appropriate. Doesn't parse and fix + strings nested in f-strings. + """ + value = s.lstrip(STRING_PREFIX_CHARS) + if value[:3] == '"""': + return s + + elif value[:3] == "'''": + orig_quote = "'''" + new_quote = '"""' + elif value[0] == '"': + orig_quote = '"' + new_quote = "'" + else: + orig_quote = "'" + new_quote = '"' + first_quote_pos = s.find(orig_quote) + if first_quote_pos == -1: + return s # There's an internal error + + prefix = s[:first_quote_pos] + unescaped_new_quote = _cached_compile(rf"(([^\\]|^)(\\\\)*){new_quote}") + escaped_new_quote = _cached_compile(rf"([^\\]|^)\\((?:\\\\)*){new_quote}") + escaped_orig_quote = _cached_compile(rf"([^\\]|^)\\((?:\\\\)*){orig_quote}") + body = s[first_quote_pos + len(orig_quote) : -len(orig_quote)] + if "r" in prefix.casefold(): + if unescaped_new_quote.search(body): + # There's at least one unescaped new_quote in this raw string + # so converting is impossible + return s + + # Do not introduce or remove backslashes in raw strings + new_body = body + else: + # remove unnecessary escapes + new_body = sub_twice(escaped_new_quote, rf"\1\2{new_quote}", body) + if body != new_body: + # Consider the string without unnecessary escapes as the original + body = new_body + s = f"{prefix}{orig_quote}{body}{orig_quote}" + new_body = sub_twice(escaped_orig_quote, rf"\1\2{orig_quote}", new_body) + new_body = sub_twice(unescaped_new_quote, rf"\1\\{new_quote}", new_body) + if "f" in prefix.casefold(): + matches = re.findall( + r""" + (?:(? orig_escape_count: + return s # Do not introduce more escaping + + if new_escape_count == orig_escape_count and orig_quote == '"': + return s # Prefer double quotes + + return f"{prefix}{new_quote}{new_body}{new_quote}" + + +def normalize_unicode_escape_sequences(leaf: Leaf) -> None: + """Replace hex codes in Unicode escape sequences with lowercase representation.""" + text = leaf.value + prefix = get_string_prefix(text) + if "r" in prefix.lower(): + return + + def replace(m: Match[str]) -> str: + groups = m.groupdict() + back_slashes = groups["backslashes"] + + if len(back_slashes) % 2 == 0: + return back_slashes + groups["body"] + + if groups["u"]: + # \u + return back_slashes + "u" + groups["u"].lower() + elif groups["U"]: + # \U + return back_slashes + "U" + groups["U"].lower() + elif groups["x"]: + # \x + return back_slashes + "x" + groups["x"].lower() + else: + assert groups["N"], f"Unexpected match: {m}" + # \N{} + return back_slashes + "N{" + groups["N"].upper() + "}" + + leaf.value = re.sub(UNICODE_ESCAPE_RE, replace, text) diff --git a/.venv/lib/python3.11/site-packages/black/trans.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.11/site-packages/black/trans.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..6a576b2f0f956628c0067f7956fde3c5159597fb GIT binary patch literal 8256 zcmeHMYit}>6~61WlTG8;CM1pXkTL=nY(&P6Uv-I0*N?TwiJh8_!=u6-k9XGgl6{!n z+0;&XSV~%Asz@O}P}N_CfIt;L0uq%9{BYya2L52)K`N4!5@DA}O`5`iZ@7{waES*Dr3JJ9z5VwWT3!0_EG_;rTy*b<8KqySE{>9Ufec z?$GaV{q(gu9 znpctPmw}e@V*##_j+b<7f`Wgc;V9 z#lK(Tm7qZaABI0iB!_eI9A#*O<~58F-MpaK!iCk zGGyA(baXP7aiZywp`Lgu86C04<55>uT4F|KEOcPSV`o6|=xH-d9(v+dCKJtw;n@Lb z#WZDO&?xav+Df9Z$C}P$<56=UF`Y^~D3ulBhf+2Og*iNH_C~FEJQWe~*my+Gr1W-S zMs3TnU{K?kj5{WPlD6m{9O&*boAqYBjV8oH%7m}r4@HRl94POR?7kRm=`!(Gvg31w zr+=8{RnYmtcn#q-Do*_MYE62?<;A_qWyOQ@8Gy1?5ANL(w>>y_1@+fGIQI$hTH1f8 zOGg00Li6DG+vZa5!M*)y^5EWcyTgNf+cnPK3K&mU{CJxtj3*bJiq#{~f^qhxz>?ci zuI(y_au2)&@9Kj=s3AXv!nKtglm~u-JWkcxvgCh+JPu)PQS#3rk6mB8BKdD4k6m6{ z0N%Jzu>gQ^q4N@ulSae3F@MuIyI%Ep+2|5Hhjy*uh+BEc=sCtH2w`38}l#cZlL{KX9L)Y>_?5Woqq%|G&z6Msf6}#Y|+H( zD0FFc3SOfxR|rt_y|si^6MFT*bI@f0Q`30`6ovB<=-k(&|3zotNw9wwVDOb&9$otk zIH*22hx%*BpuS;#aTqke4EXA6@4(<;U>`Pefo#B-e`WPsh+OD=0;GrUu3jd&|Iysa z;l)Rx8*?BKXx|AmFW<1YTzgzVu#YJAfXcPTrEMQ?dFVmgZf}h?HI27f6BErXwrM72 zr)MMLU1U0*Mdo!fy%Optb59dYjhVbhxdly}3oJ4@Ygy zupAdZ)Y{r0B5^A@shy6dGqF@sYtdWvX06__CNquU&|s+RM96GuZM0KP^C5X1X;3?* z@P3HE`5x~T`5{8)eN>411@E6iyj<`;E5s`VuU8?yMesTo;#&ppmqI*{-&cir@jVgc zesP}&()mHRtBfcg0e1n&LO_6;5y;z`HC^B4^A7-8}RvXLgK~qd4~M(c@3;APr=&< zw_QGO2^QalN~v*eHr`)KKhsS4R6Zvx@j_kX9s_(QeC5>qjJ=6|7I3ZDjW{R$6z6lkv5BAW zNxV3}^aJvr>KhC9`apJX6zk z1=+O{={?!m_mLY62EA!HH}fd;%xuE3#(_F%mrijlnR24~WHPJEWjGYGh2*BJ%#_gW z*(6xH)JeM%iVO-E6J+UV+(HAYO~)M}a}7dwqBBsJ*-t&4lDR}ZIz_u=$_88JTnl$+ zxfWdiLIl*%6x#tWvQJhb7J+`Gz&pHj*(BsBg$|oI0UJ~K+5Fcmen0Rh&haV7Gfe9g z&lEo9QT)!_g;=tNkap!iEaU*q-Azri-M= z?W^NYl6^Pn@%@46jK?1T2cX)c9Os0?2aPIB^Y3&nfo&{bb7bB-U5^|8=r&CIyaHnI2<9qTC&(_^VNKd7t*b1AE*u+&;(g{QucSS}2}(?l GYX2X17Gci- literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.11/site-packages/black/trans.py b/.venv/lib/python3.11/site-packages/black/trans.py new file mode 100644 index 00000000..2360c13f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/black/trans.py @@ -0,0 +1,2392 @@ +""" +String transformers that can split and merge strings. +""" +import re +import sys +from abc import ABC, abstractmethod +from collections import defaultdict +from dataclasses import dataclass +from typing import ( + Any, + Callable, + ClassVar, + Collection, + Dict, + Iterable, + Iterator, + List, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + Union, +) + +if sys.version_info < (3, 8): + from typing_extensions import Final, Literal +else: + from typing import Literal, Final + +from mypy_extensions import trait + +from black.comments import contains_pragma_comment +from black.lines import Line, append_leaves +from black.mode import Feature +from black.nodes import ( + CLOSING_BRACKETS, + OPENING_BRACKETS, + STANDALONE_COMMENT, + is_empty_lpar, + is_empty_par, + is_empty_rpar, + is_part_of_annotation, + parent_type, + replace_child, + syms, +) +from black.rusty import Err, Ok, Result +from black.strings import ( + assert_is_leaf_string, + get_string_prefix, + has_triple_quotes, + normalize_string_quotes, +) +from blib2to3.pgen2 import token +from blib2to3.pytree import Leaf, Node + + +class CannotTransform(Exception): + """Base class for errors raised by Transformers.""" + + +# types +T = TypeVar("T") +LN = Union[Leaf, Node] +Transformer = Callable[[Line, Collection[Feature]], Iterator[Line]] +Index = int +NodeType = int +ParserState = int +StringID = int +TResult = Result[T, CannotTransform] # (T)ransform Result +TMatchResult = TResult[List[Index]] + + +def TErr(err_msg: str) -> Err[CannotTransform]: + """(T)ransform Err + + Convenience function used when working with the TResult type. + """ + cant_transform = CannotTransform(err_msg) + return Err(cant_transform) + + +def hug_power_op(line: Line, features: Collection[Feature]) -> Iterator[Line]: + """A transformer which normalizes spacing around power operators.""" + + # Performance optimization to avoid unnecessary Leaf clones and other ops. + for leaf in line.leaves: + if leaf.type == token.DOUBLESTAR: + break + else: + raise CannotTransform("No doublestar token was found in the line.") + + def is_simple_lookup(index: int, step: Literal[1, -1]) -> bool: + # Brackets and parentheses indicate calls, subscripts, etc. ... + # basically stuff that doesn't count as "simple". Only a NAME lookup + # or dotted lookup (eg. NAME.NAME) is OK. + if step == -1: + disallowed = {token.RPAR, token.RSQB} + else: + disallowed = {token.LPAR, token.LSQB} + + while 0 <= index < len(line.leaves): + current = line.leaves[index] + if current.type in disallowed: + return False + if current.type not in {token.NAME, token.DOT} or current.value == "for": + # If the current token isn't disallowed, we'll assume this is simple as + # only the disallowed tokens are semantically attached to this lookup + # expression we're checking. Also, stop early if we hit the 'for' bit + # of a comprehension. + return True + + index += step + + return True + + def is_simple_operand(index: int, kind: Literal["base", "exponent"]) -> bool: + # An operand is considered "simple" if's a NAME, a numeric CONSTANT, a simple + # lookup (see above), with or without a preceding unary operator. + start = line.leaves[index] + if start.type in {token.NAME, token.NUMBER}: + return is_simple_lookup(index, step=(1 if kind == "exponent" else -1)) + + if start.type in {token.PLUS, token.MINUS, token.TILDE}: + if line.leaves[index + 1].type in {token.NAME, token.NUMBER}: + # step is always one as bases with a preceding unary op will be checked + # for simplicity starting from the next token (so it'll hit the check + # above). + return is_simple_lookup(index + 1, step=1) + + return False + + new_line = line.clone() + should_hug = False + for idx, leaf in enumerate(line.leaves): + new_leaf = leaf.clone() + if should_hug: + new_leaf.prefix = "" + should_hug = False + + should_hug = ( + (0 < idx < len(line.leaves) - 1) + and leaf.type == token.DOUBLESTAR + and is_simple_operand(idx - 1, kind="base") + and line.leaves[idx - 1].value != "lambda" + and is_simple_operand(idx + 1, kind="exponent") + ) + if should_hug: + new_leaf.prefix = "" + + # We have to be careful to make a new line properly: + # - bracket related metadata must be maintained (handled by Line.append) + # - comments need to copied over, updating the leaf IDs they're attached to + new_line.append(new_leaf, preformatted=True) + for comment_leaf in line.comments_after(leaf): + new_line.append(comment_leaf, preformatted=True) + + yield new_line + + +class StringTransformer(ABC): + """ + An implementation of the Transformer protocol that relies on its + subclasses overriding the template methods `do_match(...)` and + `do_transform(...)`. + + This Transformer works exclusively on strings (for example, by merging + or splitting them). + + The following sections can be found among the docstrings of each concrete + StringTransformer subclass. + + Requirements: + Which requirements must be met of the given Line for this + StringTransformer to be applied? + + Transformations: + If the given Line meets all of the above requirements, which string + transformations can you expect to be applied to it by this + StringTransformer? + + Collaborations: + What contractual agreements does this StringTransformer have with other + StringTransfomers? Such collaborations should be eliminated/minimized + as much as possible. + """ + + __name__: Final = "StringTransformer" + + # Ideally this would be a dataclass, but unfortunately mypyc breaks when used with + # `abc.ABC`. + def __init__(self, line_length: int, normalize_strings: bool) -> None: + self.line_length = line_length + self.normalize_strings = normalize_strings + + @abstractmethod + def do_match(self, line: Line) -> TMatchResult: + """ + Returns: + * Ok(string_indices) such that for each index, `line.leaves[index]` + is our target string if a match was able to be made. For + transformers that don't result in more lines (e.g. StringMerger, + StringParenStripper), multiple matches and transforms are done at + once to reduce the complexity. + OR + * Err(CannotTransform), if no match could be made. + """ + + @abstractmethod + def do_transform( + self, line: Line, string_indices: List[int] + ) -> Iterator[TResult[Line]]: + """ + Yields: + * Ok(new_line) where new_line is the new transformed line. + OR + * Err(CannotTransform) if the transformation failed for some reason. The + `do_match(...)` template method should usually be used to reject + the form of the given Line, but in some cases it is difficult to + know whether or not a Line meets the StringTransformer's + requirements until the transformation is already midway. + + Side Effects: + This method should NOT mutate @line directly, but it MAY mutate the + Line's underlying Node structure. (WARNING: If the underlying Node + structure IS altered, then this method should NOT be allowed to + yield an CannotTransform after that point.) + """ + + def __call__(self, line: Line, _features: Collection[Feature]) -> Iterator[Line]: + """ + StringTransformer instances have a call signature that mirrors that of + the Transformer type. + + Raises: + CannotTransform(...) if the concrete StringTransformer class is unable + to transform @line. + """ + # Optimization to avoid calling `self.do_match(...)` when the line does + # not contain any string. + if not any(leaf.type == token.STRING for leaf in line.leaves): + raise CannotTransform("There are no strings in this line.") + + match_result = self.do_match(line) + + if isinstance(match_result, Err): + cant_transform = match_result.err() + raise CannotTransform( + f"The string transformer {self.__class__.__name__} does not recognize" + " this line as one that it can transform." + ) from cant_transform + + string_indices = match_result.ok() + + for line_result in self.do_transform(line, string_indices): + if isinstance(line_result, Err): + cant_transform = line_result.err() + raise CannotTransform( + "StringTransformer failed while attempting to transform string." + ) from cant_transform + line = line_result.ok() + yield line + + +@dataclass +class CustomSplit: + """A custom (i.e. manual) string split. + + A single CustomSplit instance represents a single substring. + + Examples: + Consider the following string: + ``` + "Hi there friend." + " This is a custom" + f" string {split}." + ``` + + This string will correspond to the following three CustomSplit instances: + ``` + CustomSplit(False, 16) + CustomSplit(False, 17) + CustomSplit(True, 16) + ``` + """ + + has_prefix: bool + break_idx: int + + +@trait +class CustomSplitMapMixin: + """ + This mixin class is used to map merged strings to a sequence of + CustomSplits, which will then be used to re-split the strings iff none of + the resultant substrings go over the configured max line length. + """ + + _Key: ClassVar = Tuple[StringID, str] + _CUSTOM_SPLIT_MAP: ClassVar[Dict[_Key, Tuple[CustomSplit, ...]]] = defaultdict( + tuple + ) + + @staticmethod + def _get_key(string: str) -> "CustomSplitMapMixin._Key": + """ + Returns: + A unique identifier that is used internally to map @string to a + group of custom splits. + """ + return (id(string), string) + + def add_custom_splits( + self, string: str, custom_splits: Iterable[CustomSplit] + ) -> None: + """Custom Split Map Setter Method + + Side Effects: + Adds a mapping from @string to the custom splits @custom_splits. + """ + key = self._get_key(string) + self._CUSTOM_SPLIT_MAP[key] = tuple(custom_splits) + + def pop_custom_splits(self, string: str) -> List[CustomSplit]: + """Custom Split Map Getter Method + + Returns: + * A list of the custom splits that are mapped to @string, if any + exist. + OR + * [], otherwise. + + Side Effects: + Deletes the mapping between @string and its associated custom + splits (which are returned to the caller). + """ + key = self._get_key(string) + + custom_splits = self._CUSTOM_SPLIT_MAP[key] + del self._CUSTOM_SPLIT_MAP[key] + + return list(custom_splits) + + def has_custom_splits(self, string: str) -> bool: + """ + Returns: + True iff @string is associated with a set of custom splits. + """ + key = self._get_key(string) + return key in self._CUSTOM_SPLIT_MAP + + +class StringMerger(StringTransformer, CustomSplitMapMixin): + """StringTransformer that merges strings together. + + Requirements: + (A) The line contains adjacent strings such that ALL of the validation checks + listed in StringMerger._validate_msg(...)'s docstring pass. + OR + (B) The line contains a string which uses line continuation backslashes. + + Transformations: + Depending on which of the two requirements above where met, either: + + (A) The string group associated with the target string is merged. + OR + (B) All line-continuation backslashes are removed from the target string. + + Collaborations: + StringMerger provides custom split information to StringSplitter. + """ + + def do_match(self, line: Line) -> TMatchResult: + LL = line.leaves + + is_valid_index = is_valid_index_factory(LL) + + string_indices = [] + idx = 0 + while is_valid_index(idx): + leaf = LL[idx] + if ( + leaf.type == token.STRING + and is_valid_index(idx + 1) + and LL[idx + 1].type == token.STRING + ): + if not is_part_of_annotation(leaf): + string_indices.append(idx) + + # Advance to the next non-STRING leaf. + idx += 2 + while is_valid_index(idx) and LL[idx].type == token.STRING: + idx += 1 + + elif leaf.type == token.STRING and "\\\n" in leaf.value: + string_indices.append(idx) + # Advance to the next non-STRING leaf. + idx += 1 + while is_valid_index(idx) and LL[idx].type == token.STRING: + idx += 1 + + else: + idx += 1 + + if string_indices: + return Ok(string_indices) + else: + return TErr("This line has no strings that need merging.") + + def do_transform( + self, line: Line, string_indices: List[int] + ) -> Iterator[TResult[Line]]: + new_line = line + + rblc_result = self._remove_backslash_line_continuation_chars( + new_line, string_indices + ) + if isinstance(rblc_result, Ok): + new_line = rblc_result.ok() + + msg_result = self._merge_string_group(new_line, string_indices) + if isinstance(msg_result, Ok): + new_line = msg_result.ok() + + if isinstance(rblc_result, Err) and isinstance(msg_result, Err): + msg_cant_transform = msg_result.err() + rblc_cant_transform = rblc_result.err() + cant_transform = CannotTransform( + "StringMerger failed to merge any strings in this line." + ) + + # Chain the errors together using `__cause__`. + msg_cant_transform.__cause__ = rblc_cant_transform + cant_transform.__cause__ = msg_cant_transform + + yield Err(cant_transform) + else: + yield Ok(new_line) + + @staticmethod + def _remove_backslash_line_continuation_chars( + line: Line, string_indices: List[int] + ) -> TResult[Line]: + """ + Merge strings that were split across multiple lines using + line-continuation backslashes. + + Returns: + Ok(new_line), if @line contains backslash line-continuation + characters. + OR + Err(CannotTransform), otherwise. + """ + LL = line.leaves + + indices_to_transform = [] + for string_idx in string_indices: + string_leaf = LL[string_idx] + if ( + string_leaf.type == token.STRING + and "\\\n" in string_leaf.value + and not has_triple_quotes(string_leaf.value) + ): + indices_to_transform.append(string_idx) + + if not indices_to_transform: + return TErr( + "Found no string leaves that contain backslash line continuation" + " characters." + ) + + new_line = line.clone() + new_line.comments = line.comments.copy() + append_leaves(new_line, line, LL) + + for string_idx in indices_to_transform: + new_string_leaf = new_line.leaves[string_idx] + new_string_leaf.value = new_string_leaf.value.replace("\\\n", "") + + return Ok(new_line) + + def _merge_string_group( + self, line: Line, string_indices: List[int] + ) -> TResult[Line]: + """ + Merges string groups (i.e. set of adjacent strings). + + Each index from `string_indices` designates one string group's first + leaf in `line.leaves`. + + Returns: + Ok(new_line), if ALL of the validation checks found in + _validate_msg(...) pass. + OR + Err(CannotTransform), otherwise. + """ + LL = line.leaves + + is_valid_index = is_valid_index_factory(LL) + + # A dict of {string_idx: tuple[num_of_strings, string_leaf]}. + merged_string_idx_dict: Dict[int, Tuple[int, Leaf]] = {} + for string_idx in string_indices: + vresult = self._validate_msg(line, string_idx) + if isinstance(vresult, Err): + continue + merged_string_idx_dict[string_idx] = self._merge_one_string_group( + LL, string_idx, is_valid_index + ) + + if not merged_string_idx_dict: + return TErr("No string group is merged") + + # Build the final line ('new_line') that this method will later return. + new_line = line.clone() + previous_merged_string_idx = -1 + previous_merged_num_of_strings = -1 + for i, leaf in enumerate(LL): + if i in merged_string_idx_dict: + previous_merged_string_idx = i + previous_merged_num_of_strings, string_leaf = merged_string_idx_dict[i] + new_line.append(string_leaf) + + if ( + previous_merged_string_idx + <= i + < previous_merged_string_idx + previous_merged_num_of_strings + ): + for comment_leaf in line.comments_after(LL[i]): + new_line.append(comment_leaf, preformatted=True) + continue + + append_leaves(new_line, line, [leaf]) + + return Ok(new_line) + + def _merge_one_string_group( + self, LL: List[Leaf], string_idx: int, is_valid_index: Callable[[int], bool] + ) -> Tuple[int, Leaf]: + """ + Merges one string group where the first string in the group is + `LL[string_idx]`. + + Returns: + A tuple of `(num_of_strings, leaf)` where `num_of_strings` is the + number of strings merged and `leaf` is the newly merged string + to be replaced in the new line. + """ + # If the string group is wrapped inside an Atom node, we must make sure + # to later replace that Atom with our new (merged) string leaf. + atom_node = LL[string_idx].parent + + # We will place BREAK_MARK in between every two substrings that we + # merge. We will then later go through our final result and use the + # various instances of BREAK_MARK we find to add the right values to + # the custom split map. + BREAK_MARK = "@@@@@ BLACK BREAKPOINT MARKER @@@@@" + + QUOTE = LL[string_idx].value[-1] + + def make_naked(string: str, string_prefix: str) -> str: + """Strip @string (i.e. make it a "naked" string) + + Pre-conditions: + * assert_is_leaf_string(@string) + + Returns: + A string that is identical to @string except that + @string_prefix has been stripped, the surrounding QUOTE + characters have been removed, and any remaining QUOTE + characters have been escaped. + """ + assert_is_leaf_string(string) + if "f" in string_prefix: + string = _toggle_fexpr_quotes(string, QUOTE) + # After quotes toggling, quotes in expressions won't be escaped + # because quotes can't be reused in f-strings. So we can simply + # let the escaping logic below run without knowing f-string + # expressions. + + RE_EVEN_BACKSLASHES = r"(?:(?= 0 + ), "Logic error while filling the custom string breakpoint cache." + + temp_string = temp_string[mark_idx + len(BREAK_MARK) :] + breakpoint_idx = mark_idx + (len(prefix) if has_prefix else 0) + 1 + custom_splits.append(CustomSplit(has_prefix, breakpoint_idx)) + + string_leaf = Leaf(token.STRING, S_leaf.value.replace(BREAK_MARK, "")) + + if atom_node is not None: + # If not all children of the atom node are merged (this can happen + # when there is a standalone comment in the middle) ... + if non_string_idx - string_idx < len(atom_node.children): + # We need to replace the old STRING leaves with the new string leaf. + first_child_idx = LL[string_idx].remove() + for idx in range(string_idx + 1, non_string_idx): + LL[idx].remove() + if first_child_idx is not None: + atom_node.insert_child(first_child_idx, string_leaf) + else: + # Else replace the atom node with the new string leaf. + replace_child(atom_node, string_leaf) + + self.add_custom_splits(string_leaf.value, custom_splits) + return num_of_strings, string_leaf + + @staticmethod + def _validate_msg(line: Line, string_idx: int) -> TResult[None]: + """Validate (M)erge (S)tring (G)roup + + Transform-time string validation logic for _merge_string_group(...). + + Returns: + * Ok(None), if ALL validation checks (listed below) pass. + OR + * Err(CannotTransform), if any of the following are true: + - The target string group does not contain ANY stand-alone comments. + - The target string is not in a string group (i.e. it has no + adjacent strings). + - The string group has more than one inline comment. + - The string group has an inline comment that appears to be a pragma. + - The set of all string prefixes in the string group is of + length greater than one and is not equal to {"", "f"}. + - The string group consists of raw strings. + - The string group is stringified type annotations. We don't want to + process stringified type annotations since pyright doesn't support + them spanning multiple string values. (NOTE: mypy, pytype, pyre do + support them, so we can change if pyright also gains support in the + future. See https://github.com/microsoft/pyright/issues/4359.) + """ + # We first check for "inner" stand-alone comments (i.e. stand-alone + # comments that have a string leaf before them AND after them). + for inc in [1, -1]: + i = string_idx + found_sa_comment = False + is_valid_index = is_valid_index_factory(line.leaves) + while is_valid_index(i) and line.leaves[i].type in [ + token.STRING, + STANDALONE_COMMENT, + ]: + if line.leaves[i].type == STANDALONE_COMMENT: + found_sa_comment = True + elif found_sa_comment: + return TErr( + "StringMerger does NOT merge string groups which contain " + "stand-alone comments." + ) + + i += inc + + num_of_inline_string_comments = 0 + set_of_prefixes = set() + num_of_strings = 0 + for leaf in line.leaves[string_idx:]: + if leaf.type != token.STRING: + # If the string group is trailed by a comma, we count the + # comments trailing the comma to be one of the string group's + # comments. + if leaf.type == token.COMMA and id(leaf) in line.comments: + num_of_inline_string_comments += 1 + break + + if has_triple_quotes(leaf.value): + return TErr("StringMerger does NOT merge multiline strings.") + + num_of_strings += 1 + prefix = get_string_prefix(leaf.value).lower() + if "r" in prefix: + return TErr("StringMerger does NOT merge raw strings.") + + set_of_prefixes.add(prefix) + + if id(leaf) in line.comments: + num_of_inline_string_comments += 1 + if contains_pragma_comment(line.comments[id(leaf)]): + return TErr("Cannot merge strings which have pragma comments.") + + if num_of_strings < 2: + return TErr( + f"Not enough strings to merge (num_of_strings={num_of_strings})." + ) + + if num_of_inline_string_comments > 1: + return TErr( + f"Too many inline string comments ({num_of_inline_string_comments})." + ) + + if len(set_of_prefixes) > 1 and set_of_prefixes != {"", "f"}: + return TErr(f"Too many different prefixes ({set_of_prefixes}).") + + return Ok(None) + + +class StringParenStripper(StringTransformer): + """StringTransformer that strips surrounding parentheses from strings. + + Requirements: + The line contains a string which is surrounded by parentheses and: + - The target string is NOT the only argument to a function call. + - The target string is NOT a "pointless" string. + - If the target string contains a PERCENT, the brackets are not + preceded or followed by an operator with higher precedence than + PERCENT. + + Transformations: + The parentheses mentioned in the 'Requirements' section are stripped. + + Collaborations: + StringParenStripper has its own inherent usefulness, but it is also + relied on to clean up the parentheses created by StringParenWrapper (in + the event that they are no longer needed). + """ + + def do_match(self, line: Line) -> TMatchResult: + LL = line.leaves + + is_valid_index = is_valid_index_factory(LL) + + string_indices = [] + + idx = -1 + while True: + idx += 1 + if idx >= len(LL): + break + leaf = LL[idx] + + # Should be a string... + if leaf.type != token.STRING: + continue + + # If this is a "pointless" string... + if ( + leaf.parent + and leaf.parent.parent + and leaf.parent.parent.type == syms.simple_stmt + ): + continue + + # Should be preceded by a non-empty LPAR... + if ( + not is_valid_index(idx - 1) + or LL[idx - 1].type != token.LPAR + or is_empty_lpar(LL[idx - 1]) + ): + continue + + # That LPAR should NOT be preceded by a function name or a closing + # bracket (which could be a function which returns a function or a + # list/dictionary that contains a function)... + if is_valid_index(idx - 2) and ( + LL[idx - 2].type == token.NAME or LL[idx - 2].type in CLOSING_BRACKETS + ): + continue + + string_idx = idx + + # Skip the string trailer, if one exists. + string_parser = StringParser() + next_idx = string_parser.parse(LL, string_idx) + + # if the leaves in the parsed string include a PERCENT, we need to + # make sure the initial LPAR is NOT preceded by an operator with + # higher or equal precedence to PERCENT + if is_valid_index(idx - 2): + # mypy can't quite follow unless we name this + before_lpar = LL[idx - 2] + if token.PERCENT in {leaf.type for leaf in LL[idx - 1 : next_idx]} and ( + ( + before_lpar.type + in { + token.STAR, + token.AT, + token.SLASH, + token.DOUBLESLASH, + token.PERCENT, + token.TILDE, + token.DOUBLESTAR, + token.AWAIT, + token.LSQB, + token.LPAR, + } + ) + or ( + # only unary PLUS/MINUS + before_lpar.parent + and before_lpar.parent.type == syms.factor + and (before_lpar.type in {token.PLUS, token.MINUS}) + ) + ): + continue + + # Should be followed by a non-empty RPAR... + if ( + is_valid_index(next_idx) + and LL[next_idx].type == token.RPAR + and not is_empty_rpar(LL[next_idx]) + ): + # That RPAR should NOT be followed by anything with higher + # precedence than PERCENT + if is_valid_index(next_idx + 1) and LL[next_idx + 1].type in { + token.DOUBLESTAR, + token.LSQB, + token.LPAR, + token.DOT, + }: + continue + + string_indices.append(string_idx) + idx = string_idx + while idx < len(LL) - 1 and LL[idx + 1].type == token.STRING: + idx += 1 + + if string_indices: + return Ok(string_indices) + return TErr("This line has no strings wrapped in parens.") + + def do_transform( + self, line: Line, string_indices: List[int] + ) -> Iterator[TResult[Line]]: + LL = line.leaves + + string_and_rpar_indices: List[int] = [] + for string_idx in string_indices: + string_parser = StringParser() + rpar_idx = string_parser.parse(LL, string_idx) + + should_transform = True + for leaf in (LL[string_idx - 1], LL[rpar_idx]): + if line.comments_after(leaf): + # Should not strip parentheses which have comments attached + # to them. + should_transform = False + break + if should_transform: + string_and_rpar_indices.extend((string_idx, rpar_idx)) + + if string_and_rpar_indices: + yield Ok(self._transform_to_new_line(line, string_and_rpar_indices)) + else: + yield Err( + CannotTransform("All string groups have comments attached to them.") + ) + + def _transform_to_new_line( + self, line: Line, string_and_rpar_indices: List[int] + ) -> Line: + LL = line.leaves + + new_line = line.clone() + new_line.comments = line.comments.copy() + + previous_idx = -1 + # We need to sort the indices, since string_idx and its matching + # rpar_idx may not come in order, e.g. in + # `("outer" % ("inner".join(items)))`, the "inner" string's + # string_idx is smaller than "outer" string's rpar_idx. + for idx in sorted(string_and_rpar_indices): + leaf = LL[idx] + lpar_or_rpar_idx = idx - 1 if leaf.type == token.STRING else idx + append_leaves(new_line, line, LL[previous_idx + 1 : lpar_or_rpar_idx]) + if leaf.type == token.STRING: + string_leaf = Leaf(token.STRING, LL[idx].value) + LL[lpar_or_rpar_idx].remove() # Remove lpar. + replace_child(LL[idx], string_leaf) + new_line.append(string_leaf) + else: + LL[lpar_or_rpar_idx].remove() # This is a rpar. + + previous_idx = idx + + # Append the leaves after the last idx: + append_leaves(new_line, line, LL[idx + 1 :]) + + return new_line + + +class BaseStringSplitter(StringTransformer): + """ + Abstract class for StringTransformers which transform a Line's strings by splitting + them or placing them on their own lines where necessary to avoid going over + the configured line length. + + Requirements: + * The target string value is responsible for the line going over the + line length limit. It follows that after all of black's other line + split methods have been exhausted, this line (or one of the resulting + lines after all line splits are performed) would still be over the + line_length limit unless we split this string. + AND + * The target string is NOT a "pointless" string (i.e. a string that has + no parent or siblings). + AND + * The target string is not followed by an inline comment that appears + to be a pragma. + AND + * The target string is not a multiline (i.e. triple-quote) string. + """ + + STRING_OPERATORS: Final = [ + token.EQEQUAL, + token.GREATER, + token.GREATEREQUAL, + token.LESS, + token.LESSEQUAL, + token.NOTEQUAL, + token.PERCENT, + token.PLUS, + token.STAR, + ] + + @abstractmethod + def do_splitter_match(self, line: Line) -> TMatchResult: + """ + BaseStringSplitter asks its clients to override this method instead of + `StringTransformer.do_match(...)`. + + Follows the same protocol as `StringTransformer.do_match(...)`. + + Refer to `help(StringTransformer.do_match)` for more information. + """ + + def do_match(self, line: Line) -> TMatchResult: + match_result = self.do_splitter_match(line) + if isinstance(match_result, Err): + return match_result + + string_indices = match_result.ok() + assert len(string_indices) == 1, ( + f"{self.__class__.__name__} should only find one match at a time, found" + f" {len(string_indices)}" + ) + string_idx = string_indices[0] + vresult = self._validate(line, string_idx) + if isinstance(vresult, Err): + return vresult + + return match_result + + def _validate(self, line: Line, string_idx: int) -> TResult[None]: + """ + Checks that @line meets all of the requirements listed in this classes' + docstring. Refer to `help(BaseStringSplitter)` for a detailed + description of those requirements. + + Returns: + * Ok(None), if ALL of the requirements are met. + OR + * Err(CannotTransform), if ANY of the requirements are NOT met. + """ + LL = line.leaves + + string_leaf = LL[string_idx] + + max_string_length = self._get_max_string_length(line, string_idx) + if len(string_leaf.value) <= max_string_length: + return TErr( + "The string itself is not what is causing this line to be too long." + ) + + if not string_leaf.parent or [L.type for L in string_leaf.parent.children] == [ + token.STRING, + token.NEWLINE, + ]: + return TErr( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + + if id(line.leaves[string_idx]) in line.comments and contains_pragma_comment( + line.comments[id(line.leaves[string_idx])] + ): + return TErr( + "Line appears to end with an inline pragma comment. Splitting the line" + " could modify the pragma's behavior." + ) + + if has_triple_quotes(string_leaf.value): + return TErr("We cannot split multiline strings.") + + return Ok(None) + + def _get_max_string_length(self, line: Line, string_idx: int) -> int: + """ + Calculates the max string length used when attempting to determine + whether or not the target string is responsible for causing the line to + go over the line length limit. + + WARNING: This method is tightly coupled to both StringSplitter and + (especially) StringParenWrapper. There is probably a better way to + accomplish what is being done here. + + Returns: + max_string_length: such that `line.leaves[string_idx].value > + max_string_length` implies that the target string IS responsible + for causing this line to exceed the line length limit. + """ + LL = line.leaves + + is_valid_index = is_valid_index_factory(LL) + + # We use the shorthand "WMA4" in comments to abbreviate "We must + # account for". When giving examples, we use STRING to mean some/any + # valid string. + # + # Finally, we use the following convenience variables: + # + # P: The leaf that is before the target string leaf. + # N: The leaf that is after the target string leaf. + # NN: The leaf that is after N. + + # WMA4 the whitespace at the beginning of the line. + offset = line.depth * 4 + + if is_valid_index(string_idx - 1): + p_idx = string_idx - 1 + if ( + LL[string_idx - 1].type == token.LPAR + and LL[string_idx - 1].value == "" + and string_idx >= 2 + ): + # If the previous leaf is an empty LPAR placeholder, we should skip it. + p_idx -= 1 + + P = LL[p_idx] + if P.type in self.STRING_OPERATORS: + # WMA4 a space and a string operator (e.g. `+ STRING` or `== STRING`). + offset += len(str(P)) + 1 + + if P.type == token.COMMA: + # WMA4 a space, a comma, and a closing bracket [e.g. `), STRING`]. + offset += 3 + + if P.type in [token.COLON, token.EQUAL, token.PLUSEQUAL, token.NAME]: + # This conditional branch is meant to handle dictionary keys, + # variable assignments, 'return STRING' statement lines, and + # 'else STRING' ternary expression lines. + + # WMA4 a single space. + offset += 1 + + # WMA4 the lengths of any leaves that came before that space, + # but after any closing bracket before that space. + for leaf in reversed(LL[: p_idx + 1]): + offset += len(str(leaf)) + if leaf.type in CLOSING_BRACKETS: + break + + if is_valid_index(string_idx + 1): + N = LL[string_idx + 1] + if N.type == token.RPAR and N.value == "" and len(LL) > string_idx + 2: + # If the next leaf is an empty RPAR placeholder, we should skip it. + N = LL[string_idx + 2] + + if N.type == token.COMMA: + # WMA4 a single comma at the end of the string (e.g `STRING,`). + offset += 1 + + if is_valid_index(string_idx + 2): + NN = LL[string_idx + 2] + + if N.type == token.DOT and NN.type == token.NAME: + # This conditional branch is meant to handle method calls invoked + # off of a string literal up to and including the LPAR character. + + # WMA4 the '.' character. + offset += 1 + + if ( + is_valid_index(string_idx + 3) + and LL[string_idx + 3].type == token.LPAR + ): + # WMA4 the left parenthesis character. + offset += 1 + + # WMA4 the length of the method's name. + offset += len(NN.value) + + has_comments = False + for comment_leaf in line.comments_after(LL[string_idx]): + if not has_comments: + has_comments = True + # WMA4 two spaces before the '#' character. + offset += 2 + + # WMA4 the length of the inline comment. + offset += len(comment_leaf.value) + + max_string_length = self.line_length - offset + return max_string_length + + @staticmethod + def _prefer_paren_wrap_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the "prefer paren wrap" statement + requirements listed in the 'Requirements' section of the StringParenWrapper + class's docstring. + OR + None, otherwise. + """ + # The line must start with a string. + if LL[0].type != token.STRING: + return None + + # If the string is surrounded by commas (or is the first/last child)... + prev_sibling = LL[0].prev_sibling + next_sibling = LL[0].next_sibling + if not prev_sibling and not next_sibling and parent_type(LL[0]) == syms.atom: + # If it's an atom string, we need to check the parent atom's siblings. + parent = LL[0].parent + assert parent is not None # For type checkers. + prev_sibling = parent.prev_sibling + next_sibling = parent.next_sibling + if (not prev_sibling or prev_sibling.type == token.COMMA) and ( + not next_sibling or next_sibling.type == token.COMMA + ): + return 0 + + return None + + +def iter_fexpr_spans(s: str) -> Iterator[Tuple[int, int]]: + """ + Yields spans corresponding to expressions in a given f-string. + Spans are half-open ranges (left inclusive, right exclusive). + Assumes the input string is a valid f-string, but will not crash if the input + string is invalid. + """ + stack: List[int] = [] # our curly paren stack + i = 0 + while i < len(s): + if s[i] == "{": + # if we're in a string part of the f-string, ignore escaped curly braces + if not stack and i + 1 < len(s) and s[i + 1] == "{": + i += 2 + continue + stack.append(i) + i += 1 + continue + + if s[i] == "}": + if not stack: + i += 1 + continue + j = stack.pop() + # we've made it back out of the expression! yield the span + if not stack: + yield (j, i + 1) + i += 1 + continue + + # if we're in an expression part of the f-string, fast forward through strings + # note that backslashes are not legal in the expression portion of f-strings + if stack: + delim = None + if s[i : i + 3] in ("'''", '"""'): + delim = s[i : i + 3] + elif s[i] in ("'", '"'): + delim = s[i] + if delim: + i += len(delim) + while i < len(s) and s[i : i + len(delim)] != delim: + i += 1 + i += len(delim) + continue + i += 1 + + +def fstring_contains_expr(s: str) -> bool: + return any(iter_fexpr_spans(s)) + + +def _toggle_fexpr_quotes(fstring: str, old_quote: str) -> str: + """ + Toggles quotes used in f-string expressions that are `old_quote`. + + f-string expressions can't contain backslashes, so we need to toggle the + quotes if the f-string itself will end up using the same quote. We can + simply toggle without escaping because, quotes can't be reused in f-string + expressions. They will fail to parse. + + NOTE: If PEP 701 is accepted, above statement will no longer be true. + Though if quotes can be reused, we can simply reuse them without updates or + escaping, once Black figures out how to parse the new grammar. + """ + new_quote = "'" if old_quote == '"' else '"' + parts = [] + previous_index = 0 + for start, end in iter_fexpr_spans(fstring): + parts.append(fstring[previous_index:start]) + parts.append(fstring[start:end].replace(old_quote, new_quote)) + previous_index = end + parts.append(fstring[previous_index:]) + return "".join(parts) + + +class StringSplitter(BaseStringSplitter, CustomSplitMapMixin): + """ + StringTransformer that splits "atom" strings (i.e. strings which exist on + lines by themselves). + + Requirements: + * The line consists ONLY of a single string (possibly prefixed by a + string operator [e.g. '+' or '==']), MAYBE a string trailer, and MAYBE + a trailing comma. + AND + * All of the requirements listed in BaseStringSplitter's docstring. + + Transformations: + The string mentioned in the 'Requirements' section is split into as + many substrings as necessary to adhere to the configured line length. + + In the final set of substrings, no substring should be smaller than + MIN_SUBSTR_SIZE characters. + + The string will ONLY be split on spaces (i.e. each new substring should + start with a space). Note that the string will NOT be split on a space + which is escaped with a backslash. + + If the string is an f-string, it will NOT be split in the middle of an + f-expression (e.g. in f"FooBar: {foo() if x else bar()}", {foo() if x + else bar()} is an f-expression). + + If the string that is being split has an associated set of custom split + records and those custom splits will NOT result in any line going over + the configured line length, those custom splits are used. Otherwise the + string is split as late as possible (from left-to-right) while still + adhering to the transformation rules listed above. + + Collaborations: + StringSplitter relies on StringMerger to construct the appropriate + CustomSplit objects and add them to the custom split map. + """ + + MIN_SUBSTR_SIZE: Final = 6 + + def do_splitter_match(self, line: Line) -> TMatchResult: + LL = line.leaves + + if self._prefer_paren_wrap_match(LL) is not None: + return TErr("Line needs to be wrapped in parens first.") + + is_valid_index = is_valid_index_factory(LL) + + idx = 0 + + # The first two leaves MAY be the 'not in' keywords... + if ( + is_valid_index(idx) + and is_valid_index(idx + 1) + and [LL[idx].type, LL[idx + 1].type] == [token.NAME, token.NAME] + and str(LL[idx]) + str(LL[idx + 1]) == "not in" + ): + idx += 2 + # Else the first leaf MAY be a string operator symbol or the 'in' keyword... + elif is_valid_index(idx) and ( + LL[idx].type in self.STRING_OPERATORS + or LL[idx].type == token.NAME + and str(LL[idx]) == "in" + ): + idx += 1 + + # The next/first leaf MAY be an empty LPAR... + if is_valid_index(idx) and is_empty_lpar(LL[idx]): + idx += 1 + + # The next/first leaf MUST be a string... + if not is_valid_index(idx) or LL[idx].type != token.STRING: + return TErr("Line does not start with a string.") + + string_idx = idx + + # Skip the string trailer, if one exists. + string_parser = StringParser() + idx = string_parser.parse(LL, string_idx) + + # That string MAY be followed by an empty RPAR... + if is_valid_index(idx) and is_empty_rpar(LL[idx]): + idx += 1 + + # That string / empty RPAR leaf MAY be followed by a comma... + if is_valid_index(idx) and LL[idx].type == token.COMMA: + idx += 1 + + # But no more leaves are allowed... + if is_valid_index(idx): + return TErr("This line does not end with a string.") + + return Ok([string_idx]) + + def do_transform( + self, line: Line, string_indices: List[int] + ) -> Iterator[TResult[Line]]: + LL = line.leaves + assert len(string_indices) == 1, ( + f"{self.__class__.__name__} should only find one match at a time, found" + f" {len(string_indices)}" + ) + string_idx = string_indices[0] + + QUOTE = LL[string_idx].value[-1] + + is_valid_index = is_valid_index_factory(LL) + insert_str_child = insert_str_child_factory(LL[string_idx]) + + prefix = get_string_prefix(LL[string_idx].value).lower() + + # We MAY choose to drop the 'f' prefix from substrings that don't + # contain any f-expressions, but ONLY if the original f-string + # contains at least one f-expression. Otherwise, we will alter the AST + # of the program. + drop_pointless_f_prefix = ("f" in prefix) and fstring_contains_expr( + LL[string_idx].value + ) + + first_string_line = True + + string_op_leaves = self._get_string_operator_leaves(LL) + string_op_leaves_length = ( + sum(len(str(prefix_leaf)) for prefix_leaf in string_op_leaves) + 1 + if string_op_leaves + else 0 + ) + + def maybe_append_string_operators(new_line: Line) -> None: + """ + Side Effects: + If @line starts with a string operator and this is the first + line we are constructing, this function appends the string + operator to @new_line and replaces the old string operator leaf + in the node structure. Otherwise this function does nothing. + """ + maybe_prefix_leaves = string_op_leaves if first_string_line else [] + for i, prefix_leaf in enumerate(maybe_prefix_leaves): + replace_child(LL[i], prefix_leaf) + new_line.append(prefix_leaf) + + ends_with_comma = ( + is_valid_index(string_idx + 1) and LL[string_idx + 1].type == token.COMMA + ) + + def max_last_string() -> int: + """ + Returns: + The max allowed length of the string value used for the last + line we will construct. + """ + result = self.line_length + result -= line.depth * 4 + result -= 1 if ends_with_comma else 0 + result -= string_op_leaves_length + return result + + # --- Calculate Max Break Index (for string value) + # We start with the line length limit + max_break_idx = self.line_length + # The last index of a string of length N is N-1. + max_break_idx -= 1 + # Leading whitespace is not present in the string value (e.g. Leaf.value). + max_break_idx -= line.depth * 4 + if max_break_idx < 0: + yield TErr( + f"Unable to split {LL[string_idx].value} at such high of a line depth:" + f" {line.depth}" + ) + return + + # Check if StringMerger registered any custom splits. + custom_splits = self.pop_custom_splits(LL[string_idx].value) + # We use them ONLY if none of them would produce lines that exceed the + # line limit. + use_custom_breakpoints = bool( + custom_splits + and all(csplit.break_idx <= max_break_idx for csplit in custom_splits) + ) + + # Temporary storage for the remaining chunk of the string line that + # can't fit onto the line currently being constructed. + rest_value = LL[string_idx].value + + def more_splits_should_be_made() -> bool: + """ + Returns: + True iff `rest_value` (the remaining string value from the last + split), should be split again. + """ + if use_custom_breakpoints: + return len(custom_splits) > 1 + else: + return len(rest_value) > max_last_string() + + string_line_results: List[Ok[Line]] = [] + while more_splits_should_be_made(): + if use_custom_breakpoints: + # Custom User Split (manual) + csplit = custom_splits.pop(0) + break_idx = csplit.break_idx + else: + # Algorithmic Split (automatic) + max_bidx = max_break_idx - string_op_leaves_length + maybe_break_idx = self._get_break_idx(rest_value, max_bidx) + if maybe_break_idx is None: + # If we are unable to algorithmically determine a good split + # and this string has custom splits registered to it, we + # fall back to using them--which means we have to start + # over from the beginning. + if custom_splits: + rest_value = LL[string_idx].value + string_line_results = [] + first_string_line = True + use_custom_breakpoints = True + continue + + # Otherwise, we stop splitting here. + break + + break_idx = maybe_break_idx + + # --- Construct `next_value` + next_value = rest_value[:break_idx] + QUOTE + + # HACK: The following 'if' statement is a hack to fix the custom + # breakpoint index in the case of either: (a) substrings that were + # f-strings but will have the 'f' prefix removed OR (b) substrings + # that were not f-strings but will now become f-strings because of + # redundant use of the 'f' prefix (i.e. none of the substrings + # contain f-expressions but one or more of them had the 'f' prefix + # anyway; in which case, we will prepend 'f' to _all_ substrings). + # + # There is probably a better way to accomplish what is being done + # here... + # + # If this substring is an f-string, we _could_ remove the 'f' + # prefix, and the current custom split did NOT originally use a + # prefix... + if ( + use_custom_breakpoints + and not csplit.has_prefix + and ( + # `next_value == prefix + QUOTE` happens when the custom + # split is an empty string. + next_value == prefix + QUOTE + or next_value != self._normalize_f_string(next_value, prefix) + ) + ): + # Then `csplit.break_idx` will be off by one after removing + # the 'f' prefix. + break_idx += 1 + next_value = rest_value[:break_idx] + QUOTE + + if drop_pointless_f_prefix: + next_value = self._normalize_f_string(next_value, prefix) + + # --- Construct `next_leaf` + next_leaf = Leaf(token.STRING, next_value) + insert_str_child(next_leaf) + self._maybe_normalize_string_quotes(next_leaf) + + # --- Construct `next_line` + next_line = line.clone() + maybe_append_string_operators(next_line) + next_line.append(next_leaf) + string_line_results.append(Ok(next_line)) + + rest_value = prefix + QUOTE + rest_value[break_idx:] + first_string_line = False + + yield from string_line_results + + if drop_pointless_f_prefix: + rest_value = self._normalize_f_string(rest_value, prefix) + + rest_leaf = Leaf(token.STRING, rest_value) + insert_str_child(rest_leaf) + + # NOTE: I could not find a test case that verifies that the following + # line is actually necessary, but it seems to be. Otherwise we risk + # not normalizing the last substring, right? + self._maybe_normalize_string_quotes(rest_leaf) + + last_line = line.clone() + maybe_append_string_operators(last_line) + + # If there are any leaves to the right of the target string... + if is_valid_index(string_idx + 1): + # We use `temp_value` here to determine how long the last line + # would be if we were to append all the leaves to the right of the + # target string to the last string line. + temp_value = rest_value + for leaf in LL[string_idx + 1 :]: + temp_value += str(leaf) + if leaf.type == token.LPAR: + break + + # Try to fit them all on the same line with the last substring... + if ( + len(temp_value) <= max_last_string() + or LL[string_idx + 1].type == token.COMMA + ): + last_line.append(rest_leaf) + append_leaves(last_line, line, LL[string_idx + 1 :]) + yield Ok(last_line) + # Otherwise, place the last substring on one line and everything + # else on a line below that... + else: + last_line.append(rest_leaf) + yield Ok(last_line) + + non_string_line = line.clone() + append_leaves(non_string_line, line, LL[string_idx + 1 :]) + yield Ok(non_string_line) + # Else the target string was the last leaf... + else: + last_line.append(rest_leaf) + last_line.comments = line.comments.copy() + yield Ok(last_line) + + def _iter_nameescape_slices(self, string: str) -> Iterator[Tuple[Index, Index]]: + """ + Yields: + All ranges of @string which, if @string were to be split there, + would result in the splitting of an \\N{...} expression (which is NOT + allowed). + """ + # True - the previous backslash was unescaped + # False - the previous backslash was escaped *or* there was no backslash + previous_was_unescaped_backslash = False + it = iter(enumerate(string)) + for idx, c in it: + if c == "\\": + previous_was_unescaped_backslash = not previous_was_unescaped_backslash + continue + if not previous_was_unescaped_backslash or c != "N": + previous_was_unescaped_backslash = False + continue + previous_was_unescaped_backslash = False + + begin = idx - 1 # the position of backslash before \N{...} + for idx, c in it: + if c == "}": + end = idx + break + else: + # malformed nameescape expression? + # should have been detected by AST parsing earlier... + raise RuntimeError(f"{self.__class__.__name__} LOGIC ERROR!") + yield begin, end + + def _iter_fexpr_slices(self, string: str) -> Iterator[Tuple[Index, Index]]: + """ + Yields: + All ranges of @string which, if @string were to be split there, + would result in the splitting of an f-expression (which is NOT + allowed). + """ + if "f" not in get_string_prefix(string).lower(): + return + yield from iter_fexpr_spans(string) + + def _get_illegal_split_indices(self, string: str) -> Set[Index]: + illegal_indices: Set[Index] = set() + iterators = [ + self._iter_fexpr_slices(string), + self._iter_nameescape_slices(string), + ] + for it in iterators: + for begin, end in it: + illegal_indices.update(range(begin, end + 1)) + return illegal_indices + + def _get_break_idx(self, string: str, max_break_idx: int) -> Optional[int]: + """ + This method contains the algorithm that StringSplitter uses to + determine which character to split each string at. + + Args: + @string: The substring that we are attempting to split. + @max_break_idx: The ideal break index. We will return this value if it + meets all the necessary conditions. In the likely event that it + doesn't we will try to find the closest index BELOW @max_break_idx + that does. If that fails, we will expand our search by also + considering all valid indices ABOVE @max_break_idx. + + Pre-Conditions: + * assert_is_leaf_string(@string) + * 0 <= @max_break_idx < len(@string) + + Returns: + break_idx, if an index is able to be found that meets all of the + conditions listed in the 'Transformations' section of this classes' + docstring. + OR + None, otherwise. + """ + is_valid_index = is_valid_index_factory(string) + + assert is_valid_index(max_break_idx) + assert_is_leaf_string(string) + + _illegal_split_indices = self._get_illegal_split_indices(string) + + def breaks_unsplittable_expression(i: Index) -> bool: + """ + Returns: + True iff returning @i would result in the splitting of an + unsplittable expression (which is NOT allowed). + """ + return i in _illegal_split_indices + + def passes_all_checks(i: Index) -> bool: + """ + Returns: + True iff ALL of the conditions listed in the 'Transformations' + section of this classes' docstring would be be met by returning @i. + """ + is_space = string[i] == " " + + is_not_escaped = True + j = i - 1 + while is_valid_index(j) and string[j] == "\\": + is_not_escaped = not is_not_escaped + j -= 1 + + is_big_enough = ( + len(string[i:]) >= self.MIN_SUBSTR_SIZE + and len(string[:i]) >= self.MIN_SUBSTR_SIZE + ) + return ( + is_space + and is_not_escaped + and is_big_enough + and not breaks_unsplittable_expression(i) + ) + + # First, we check all indices BELOW @max_break_idx. + break_idx = max_break_idx + while is_valid_index(break_idx - 1) and not passes_all_checks(break_idx): + break_idx -= 1 + + if not passes_all_checks(break_idx): + # If that fails, we check all indices ABOVE @max_break_idx. + # + # If we are able to find a valid index here, the next line is going + # to be longer than the specified line length, but it's probably + # better than doing nothing at all. + break_idx = max_break_idx + 1 + while is_valid_index(break_idx + 1) and not passes_all_checks(break_idx): + break_idx += 1 + + if not is_valid_index(break_idx) or not passes_all_checks(break_idx): + return None + + return break_idx + + def _maybe_normalize_string_quotes(self, leaf: Leaf) -> None: + if self.normalize_strings: + leaf.value = normalize_string_quotes(leaf.value) + + def _normalize_f_string(self, string: str, prefix: str) -> str: + """ + Pre-Conditions: + * assert_is_leaf_string(@string) + + Returns: + * If @string is an f-string that contains no f-expressions, we + return a string identical to @string except that the 'f' prefix + has been stripped and all double braces (i.e. '{{' or '}}') have + been normalized (i.e. turned into '{' or '}'). + OR + * Otherwise, we return @string. + """ + assert_is_leaf_string(string) + + if "f" in prefix and not fstring_contains_expr(string): + new_prefix = prefix.replace("f", "") + + temp = string[len(prefix) :] + temp = re.sub(r"\{\{", "{", temp) + temp = re.sub(r"\}\}", "}", temp) + new_string = temp + + return f"{new_prefix}{new_string}" + else: + return string + + def _get_string_operator_leaves(self, leaves: Iterable[Leaf]) -> List[Leaf]: + LL = list(leaves) + + string_op_leaves = [] + i = 0 + while LL[i].type in self.STRING_OPERATORS + [token.NAME]: + prefix_leaf = Leaf(LL[i].type, str(LL[i]).strip()) + string_op_leaves.append(prefix_leaf) + i += 1 + return string_op_leaves + + +class StringParenWrapper(BaseStringSplitter, CustomSplitMapMixin): + """ + StringTransformer that wraps strings in parens and then splits at the LPAR. + + Requirements: + All of the requirements listed in BaseStringSplitter's docstring in + addition to the requirements listed below: + + * The line is a return/yield statement, which returns/yields a string. + OR + * The line is part of a ternary expression (e.g. `x = y if cond else + z`) such that the line starts with `else `, where is + some string. + OR + * The line is an assert statement, which ends with a string. + OR + * The line is an assignment statement (e.g. `x = ` or `x += + `) such that the variable is being assigned the value of some + string. + OR + * The line is a dictionary key assignment where some valid key is being + assigned the value of some string. + OR + * The line is an lambda expression and the value is a string. + OR + * The line starts with an "atom" string that prefers to be wrapped in + parens. It's preferred to be wrapped when the string is surrounded by + commas (or is the first/last child). + + Transformations: + The chosen string is wrapped in parentheses and then split at the LPAR. + + We then have one line which ends with an LPAR and another line that + starts with the chosen string. The latter line is then split again at + the RPAR. This results in the RPAR (and possibly a trailing comma) + being placed on its own line. + + NOTE: If any leaves exist to the right of the chosen string (except + for a trailing comma, which would be placed after the RPAR), those + leaves are placed inside the parentheses. In effect, the chosen + string is not necessarily being "wrapped" by parentheses. We can, + however, count on the LPAR being placed directly before the chosen + string. + + In other words, StringParenWrapper creates "atom" strings. These + can then be split again by StringSplitter, if necessary. + + Collaborations: + In the event that a string line split by StringParenWrapper is + changed such that it no longer needs to be given its own line, + StringParenWrapper relies on StringParenStripper to clean up the + parentheses it created. + + For "atom" strings that prefers to be wrapped in parens, it requires + StringSplitter to hold the split until the string is wrapped in parens. + """ + + def do_splitter_match(self, line: Line) -> TMatchResult: + LL = line.leaves + + if line.leaves[-1].type in OPENING_BRACKETS: + return TErr( + "Cannot wrap parens around a line that ends in an opening bracket." + ) + + string_idx = ( + self._return_match(LL) + or self._else_match(LL) + or self._assert_match(LL) + or self._assign_match(LL) + or self._dict_or_lambda_match(LL) + or self._prefer_paren_wrap_match(LL) + ) + + if string_idx is not None: + string_value = line.leaves[string_idx].value + # If the string has no spaces... + if " " not in string_value: + # And will still violate the line length limit when split... + max_string_length = self.line_length - ((line.depth + 1) * 4) + if len(string_value) > max_string_length: + # And has no associated custom splits... + if not self.has_custom_splits(string_value): + # Then we should NOT put this string on its own line. + return TErr( + "We do not wrap long strings in parentheses when the" + " resultant line would still be over the specified line" + " length and can't be split further by StringSplitter." + ) + return Ok([string_idx]) + + return TErr("This line does not contain any non-atomic strings.") + + @staticmethod + def _return_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the return/yield statement + requirements listed in the 'Requirements' section of this classes' + docstring. + OR + None, otherwise. + """ + # If this line is apart of a return/yield statement and the first leaf + # contains either the "return" or "yield" keywords... + if parent_type(LL[0]) in [syms.return_stmt, syms.yield_expr] and LL[ + 0 + ].value in ["return", "yield"]: + is_valid_index = is_valid_index_factory(LL) + + idx = 2 if is_valid_index(1) and is_empty_par(LL[1]) else 1 + # The next visible leaf MUST contain a string... + if is_valid_index(idx) and LL[idx].type == token.STRING: + return idx + + return None + + @staticmethod + def _else_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the ternary expression + requirements listed in the 'Requirements' section of this classes' + docstring. + OR + None, otherwise. + """ + # If this line is apart of a ternary expression and the first leaf + # contains the "else" keyword... + if ( + parent_type(LL[0]) == syms.test + and LL[0].type == token.NAME + and LL[0].value == "else" + ): + is_valid_index = is_valid_index_factory(LL) + + idx = 2 if is_valid_index(1) and is_empty_par(LL[1]) else 1 + # The next visible leaf MUST contain a string... + if is_valid_index(idx) and LL[idx].type == token.STRING: + return idx + + return None + + @staticmethod + def _assert_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the assert statement + requirements listed in the 'Requirements' section of this classes' + docstring. + OR + None, otherwise. + """ + # If this line is apart of an assert statement and the first leaf + # contains the "assert" keyword... + if parent_type(LL[0]) == syms.assert_stmt and LL[0].value == "assert": + is_valid_index = is_valid_index_factory(LL) + + for i, leaf in enumerate(LL): + # We MUST find a comma... + if leaf.type == token.COMMA: + idx = i + 2 if is_empty_par(LL[i + 1]) else i + 1 + + # That comma MUST be followed by a string... + if is_valid_index(idx) and LL[idx].type == token.STRING: + string_idx = idx + + # Skip the string trailer, if one exists. + string_parser = StringParser() + idx = string_parser.parse(LL, string_idx) + + # But no more leaves are allowed... + if not is_valid_index(idx): + return string_idx + + return None + + @staticmethod + def _assign_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the assignment statement + requirements listed in the 'Requirements' section of this classes' + docstring. + OR + None, otherwise. + """ + # If this line is apart of an expression statement or is a function + # argument AND the first leaf contains a variable name... + if ( + parent_type(LL[0]) in [syms.expr_stmt, syms.argument, syms.power] + and LL[0].type == token.NAME + ): + is_valid_index = is_valid_index_factory(LL) + + for i, leaf in enumerate(LL): + # We MUST find either an '=' or '+=' symbol... + if leaf.type in [token.EQUAL, token.PLUSEQUAL]: + idx = i + 2 if is_empty_par(LL[i + 1]) else i + 1 + + # That symbol MUST be followed by a string... + if is_valid_index(idx) and LL[idx].type == token.STRING: + string_idx = idx + + # Skip the string trailer, if one exists. + string_parser = StringParser() + idx = string_parser.parse(LL, string_idx) + + # The next leaf MAY be a comma iff this line is apart + # of a function argument... + if ( + parent_type(LL[0]) == syms.argument + and is_valid_index(idx) + and LL[idx].type == token.COMMA + ): + idx += 1 + + # But no more leaves are allowed... + if not is_valid_index(idx): + return string_idx + + return None + + @staticmethod + def _dict_or_lambda_match(LL: List[Leaf]) -> Optional[int]: + """ + Returns: + string_idx such that @LL[string_idx] is equal to our target (i.e. + matched) string, if this line matches the dictionary key assignment + statement or lambda expression requirements listed in the + 'Requirements' section of this classes' docstring. + OR + None, otherwise. + """ + # If this line is a part of a dictionary key assignment or lambda expression... + parent_types = [parent_type(LL[0]), parent_type(LL[0].parent)] + if syms.dictsetmaker in parent_types or syms.lambdef in parent_types: + is_valid_index = is_valid_index_factory(LL) + + for i, leaf in enumerate(LL): + # We MUST find a colon, it can either be dict's or lambda's colon... + if leaf.type == token.COLON and i < len(LL) - 1: + idx = i + 2 if is_empty_par(LL[i + 1]) else i + 1 + + # That colon MUST be followed by a string... + if is_valid_index(idx) and LL[idx].type == token.STRING: + string_idx = idx + + # Skip the string trailer, if one exists. + string_parser = StringParser() + idx = string_parser.parse(LL, string_idx) + + # That string MAY be followed by a comma... + if is_valid_index(idx) and LL[idx].type == token.COMMA: + idx += 1 + + # But no more leaves are allowed... + if not is_valid_index(idx): + return string_idx + + return None + + def do_transform( + self, line: Line, string_indices: List[int] + ) -> Iterator[TResult[Line]]: + LL = line.leaves + assert len(string_indices) == 1, ( + f"{self.__class__.__name__} should only find one match at a time, found" + f" {len(string_indices)}" + ) + string_idx = string_indices[0] + + is_valid_index = is_valid_index_factory(LL) + insert_str_child = insert_str_child_factory(LL[string_idx]) + + comma_idx = -1 + ends_with_comma = False + if LL[comma_idx].type == token.COMMA: + ends_with_comma = True + + leaves_to_steal_comments_from = [LL[string_idx]] + if ends_with_comma: + leaves_to_steal_comments_from.append(LL[comma_idx]) + + # --- First Line + first_line = line.clone() + left_leaves = LL[:string_idx] + + # We have to remember to account for (possibly invisible) LPAR and RPAR + # leaves that already wrapped the target string. If these leaves do + # exist, we will replace them with our own LPAR and RPAR leaves. + old_parens_exist = False + if left_leaves and left_leaves[-1].type == token.LPAR: + old_parens_exist = True + leaves_to_steal_comments_from.append(left_leaves[-1]) + left_leaves.pop() + + append_leaves(first_line, line, left_leaves) + + lpar_leaf = Leaf(token.LPAR, "(") + if old_parens_exist: + replace_child(LL[string_idx - 1], lpar_leaf) + else: + insert_str_child(lpar_leaf) + first_line.append(lpar_leaf) + + # We throw inline comments that were originally to the right of the + # target string to the top line. They will now be shown to the right of + # the LPAR. + for leaf in leaves_to_steal_comments_from: + for comment_leaf in line.comments_after(leaf): + first_line.append(comment_leaf, preformatted=True) + + yield Ok(first_line) + + # --- Middle (String) Line + # We only need to yield one (possibly too long) string line, since the + # `StringSplitter` will break it down further if necessary. + string_value = LL[string_idx].value + string_line = Line( + mode=line.mode, + depth=line.depth + 1, + inside_brackets=True, + should_split_rhs=line.should_split_rhs, + magic_trailing_comma=line.magic_trailing_comma, + ) + string_leaf = Leaf(token.STRING, string_value) + insert_str_child(string_leaf) + string_line.append(string_leaf) + + old_rpar_leaf = None + if is_valid_index(string_idx + 1): + right_leaves = LL[string_idx + 1 :] + if ends_with_comma: + right_leaves.pop() + + if old_parens_exist: + assert right_leaves and right_leaves[-1].type == token.RPAR, ( + "Apparently, old parentheses do NOT exist?!" + f" (left_leaves={left_leaves}, right_leaves={right_leaves})" + ) + old_rpar_leaf = right_leaves.pop() + elif right_leaves and right_leaves[-1].type == token.RPAR: + # Special case for lambda expressions as dict's value, e.g.: + # my_dict = { + # "key": lambda x: f"formatted: {x}, + # } + # After wrapping the dict's value with parentheses, the string is + # followed by a RPAR but its opening bracket is lambda's, not + # the string's: + # "key": (lambda x: f"formatted: {x}), + opening_bracket = right_leaves[-1].opening_bracket + if opening_bracket is not None and opening_bracket in left_leaves: + index = left_leaves.index(opening_bracket) + if ( + index > 0 + and index < len(left_leaves) - 1 + and left_leaves[index - 1].type == token.COLON + and left_leaves[index + 1].value == "lambda" + ): + right_leaves.pop() + + append_leaves(string_line, line, right_leaves) + + yield Ok(string_line) + + # --- Last Line + last_line = line.clone() + last_line.bracket_tracker = first_line.bracket_tracker + + new_rpar_leaf = Leaf(token.RPAR, ")") + if old_rpar_leaf is not None: + replace_child(old_rpar_leaf, new_rpar_leaf) + else: + insert_str_child(new_rpar_leaf) + last_line.append(new_rpar_leaf) + + # If the target string ended with a comma, we place this comma to the + # right of the RPAR on the last line. + if ends_with_comma: + comma_leaf = Leaf(token.COMMA, ",") + replace_child(LL[comma_idx], comma_leaf) + last_line.append(comma_leaf) + + yield Ok(last_line) + + +class StringParser: + """ + A state machine that aids in parsing a string's "trailer", which can be + either non-existent, an old-style formatting sequence (e.g. `% varX` or `% + (varX, varY)`), or a method-call / attribute access (e.g. `.format(varX, + varY)`). + + NOTE: A new StringParser object MUST be instantiated for each string + trailer we need to parse. + + Examples: + We shall assume that `line` equals the `Line` object that corresponds + to the following line of python code: + ``` + x = "Some {}.".format("String") + some_other_string + ``` + + Furthermore, we will assume that `string_idx` is some index such that: + ``` + assert line.leaves[string_idx].value == "Some {}." + ``` + + The following code snippet then holds: + ``` + string_parser = StringParser() + idx = string_parser.parse(line.leaves, string_idx) + assert line.leaves[idx].type == token.PLUS + ``` + """ + + DEFAULT_TOKEN: Final = 20210605 + + # String Parser States + START: Final = 1 + DOT: Final = 2 + NAME: Final = 3 + PERCENT: Final = 4 + SINGLE_FMT_ARG: Final = 5 + LPAR: Final = 6 + RPAR: Final = 7 + DONE: Final = 8 + + # Lookup Table for Next State + _goto: Final[Dict[Tuple[ParserState, NodeType], ParserState]] = { + # A string trailer may start with '.' OR '%'. + (START, token.DOT): DOT, + (START, token.PERCENT): PERCENT, + (START, DEFAULT_TOKEN): DONE, + # A '.' MUST be followed by an attribute or method name. + (DOT, token.NAME): NAME, + # A method name MUST be followed by an '(', whereas an attribute name + # is the last symbol in the string trailer. + (NAME, token.LPAR): LPAR, + (NAME, DEFAULT_TOKEN): DONE, + # A '%' symbol can be followed by an '(' or a single argument (e.g. a + # string or variable name). + (PERCENT, token.LPAR): LPAR, + (PERCENT, DEFAULT_TOKEN): SINGLE_FMT_ARG, + # If a '%' symbol is followed by a single argument, that argument is + # the last leaf in the string trailer. + (SINGLE_FMT_ARG, DEFAULT_TOKEN): DONE, + # If present, a ')' symbol is the last symbol in a string trailer. + # (NOTE: LPARS and nested RPARS are not included in this lookup table, + # since they are treated as a special case by the parsing logic in this + # classes' implementation.) + (RPAR, DEFAULT_TOKEN): DONE, + } + + def __init__(self) -> None: + self._state = self.START + self._unmatched_lpars = 0 + + def parse(self, leaves: List[Leaf], string_idx: int) -> int: + """ + Pre-conditions: + * @leaves[@string_idx].type == token.STRING + + Returns: + The index directly after the last leaf which is apart of the string + trailer, if a "trailer" exists. + OR + @string_idx + 1, if no string "trailer" exists. + """ + assert leaves[string_idx].type == token.STRING + + idx = string_idx + 1 + while idx < len(leaves) and self._next_state(leaves[idx]): + idx += 1 + return idx + + def _next_state(self, leaf: Leaf) -> bool: + """ + Pre-conditions: + * On the first call to this function, @leaf MUST be the leaf that + was directly after the string leaf in question (e.g. if our target + string is `line.leaves[i]` then the first call to this method must + be `line.leaves[i + 1]`). + * On the next call to this function, the leaf parameter passed in + MUST be the leaf directly following @leaf. + + Returns: + True iff @leaf is apart of the string's trailer. + """ + # We ignore empty LPAR or RPAR leaves. + if is_empty_par(leaf): + return True + + next_token = leaf.type + if next_token == token.LPAR: + self._unmatched_lpars += 1 + + current_state = self._state + + # The LPAR parser state is a special case. We will return True until we + # find the matching RPAR token. + if current_state == self.LPAR: + if next_token == token.RPAR: + self._unmatched_lpars -= 1 + if self._unmatched_lpars == 0: + self._state = self.RPAR + # Otherwise, we use a lookup table to determine the next state. + else: + # If the lookup table matches the current state to the next + # token, we use the lookup table. + if (current_state, next_token) in self._goto: + self._state = self._goto[current_state, next_token] + else: + # Otherwise, we check if a the current state was assigned a + # default. + if (current_state, self.DEFAULT_TOKEN) in self._goto: + self._state = self._goto[current_state, self.DEFAULT_TOKEN] + # If no default has been assigned, then this parser has a logic + # error. + else: + raise RuntimeError(f"{self.__class__.__name__} LOGIC ERROR!") + + if self._state == self.DONE: + return False + + return True + + +def insert_str_child_factory(string_leaf: Leaf) -> Callable[[LN], None]: + """ + Factory for a convenience function that is used to orphan @string_leaf + and then insert multiple new leaves into the same part of the node + structure that @string_leaf had originally occupied. + + Examples: + Let `string_leaf = Leaf(token.STRING, '"foo"')` and `N = + string_leaf.parent`. Assume the node `N` has the following + original structure: + + Node( + expr_stmt, [ + Leaf(NAME, 'x'), + Leaf(EQUAL, '='), + Leaf(STRING, '"foo"'), + ] + ) + + We then run the code snippet shown below. + ``` + insert_str_child = insert_str_child_factory(string_leaf) + + lpar = Leaf(token.LPAR, '(') + insert_str_child(lpar) + + bar = Leaf(token.STRING, '"bar"') + insert_str_child(bar) + + rpar = Leaf(token.RPAR, ')') + insert_str_child(rpar) + ``` + + After which point, it follows that `string_leaf.parent is None` and + the node `N` now has the following structure: + + Node( + expr_stmt, [ + Leaf(NAME, 'x'), + Leaf(EQUAL, '='), + Leaf(LPAR, '('), + Leaf(STRING, '"bar"'), + Leaf(RPAR, ')'), + ] + ) + """ + string_parent = string_leaf.parent + string_child_idx = string_leaf.remove() + + def insert_str_child(child: LN) -> None: + nonlocal string_child_idx + + assert string_parent is not None + assert string_child_idx is not None + + string_parent.insert_child(string_child_idx, child) + string_child_idx += 1 + + return insert_str_child + + +def is_valid_index_factory(seq: Sequence[Any]) -> Callable[[int], bool]: + """ + Examples: + ``` + my_list = [1, 2, 3] + + is_valid_index = is_valid_index_factory(my_list) + + assert is_valid_index(0) + assert is_valid_index(2) + + assert not is_valid_index(3) + assert not is_valid_index(-1) + ``` + """ + + def is_valid_index(idx: int) -> bool: + """ + Returns: + True iff @idx is positive AND seq[@idx] does NOT raise an + IndexError. + """ + return 0 <= idx < len(seq) + + return is_valid_index diff --git a/.venv/lib/python3.11/site-packages/blackd/__init__.py b/.venv/lib/python3.11/site-packages/blackd/__init__.py new file mode 100644 index 00000000..ba4750b8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/blackd/__init__.py @@ -0,0 +1,227 @@ +import asyncio +import logging +from concurrent.futures import Executor, ProcessPoolExecutor +from datetime import datetime +from functools import partial +from multiprocessing import freeze_support +from typing import Set, Tuple + +try: + from aiohttp import web + + from .middlewares import cors +except ImportError as ie: + raise ImportError( + f"aiohttp dependency is not installed: {ie}. " + + "Please re-install black with the '[d]' extra install " + + "to obtain aiohttp_cors: `pip install black[d]`" + ) from None + +import click + +import black +from _black_version import version as __version__ +from black.concurrency import maybe_install_uvloop + +# This is used internally by tests to shut down the server prematurely +_stop_signal = asyncio.Event() + +# Request headers +PROTOCOL_VERSION_HEADER = "X-Protocol-Version" +LINE_LENGTH_HEADER = "X-Line-Length" +PYTHON_VARIANT_HEADER = "X-Python-Variant" +SKIP_SOURCE_FIRST_LINE = "X-Skip-Source-First-Line" +SKIP_STRING_NORMALIZATION_HEADER = "X-Skip-String-Normalization" +SKIP_MAGIC_TRAILING_COMMA = "X-Skip-Magic-Trailing-Comma" +PREVIEW = "X-Preview" +FAST_OR_SAFE_HEADER = "X-Fast-Or-Safe" +DIFF_HEADER = "X-Diff" + +BLACK_HEADERS = [ + PROTOCOL_VERSION_HEADER, + LINE_LENGTH_HEADER, + PYTHON_VARIANT_HEADER, + SKIP_SOURCE_FIRST_LINE, + SKIP_STRING_NORMALIZATION_HEADER, + SKIP_MAGIC_TRAILING_COMMA, + PREVIEW, + FAST_OR_SAFE_HEADER, + DIFF_HEADER, +] + +# Response headers +BLACK_VERSION_HEADER = "X-Black-Version" + + +class InvalidVariantHeader(Exception): + pass + + +@click.command(context_settings={"help_option_names": ["-h", "--help"]}) +@click.option( + "--bind-host", type=str, help="Address to bind the server to.", default="localhost" +) +@click.option("--bind-port", type=int, help="Port to listen on", default=45484) +@click.version_option(version=black.__version__) +def main(bind_host: str, bind_port: int) -> None: + logging.basicConfig(level=logging.INFO) + app = make_app() + ver = black.__version__ + black.out(f"blackd version {ver} listening on {bind_host} port {bind_port}") + web.run_app(app, host=bind_host, port=bind_port, handle_signals=True, print=None) + + +def make_app() -> web.Application: + app = web.Application( + middlewares=[cors(allow_headers=(*BLACK_HEADERS, "Content-Type"))] + ) + executor = ProcessPoolExecutor() + app.add_routes([web.post("/", partial(handle, executor=executor))]) + return app + + +async def handle(request: web.Request, executor: Executor) -> web.Response: + headers = {BLACK_VERSION_HEADER: __version__} + try: + if request.headers.get(PROTOCOL_VERSION_HEADER, "1") != "1": + return web.Response( + status=501, text="This server only supports protocol version 1" + ) + try: + line_length = int( + request.headers.get(LINE_LENGTH_HEADER, black.DEFAULT_LINE_LENGTH) + ) + except ValueError: + return web.Response(status=400, text="Invalid line length header value") + + if PYTHON_VARIANT_HEADER in request.headers: + value = request.headers[PYTHON_VARIANT_HEADER] + try: + pyi, versions = parse_python_variant_header(value) + except InvalidVariantHeader as e: + return web.Response( + status=400, + text=f"Invalid value for {PYTHON_VARIANT_HEADER}: {e.args[0]}", + ) + else: + pyi = False + versions = set() + + skip_string_normalization = bool( + request.headers.get(SKIP_STRING_NORMALIZATION_HEADER, False) + ) + skip_magic_trailing_comma = bool( + request.headers.get(SKIP_MAGIC_TRAILING_COMMA, False) + ) + skip_source_first_line = bool( + request.headers.get(SKIP_SOURCE_FIRST_LINE, False) + ) + preview = bool(request.headers.get(PREVIEW, False)) + fast = False + if request.headers.get(FAST_OR_SAFE_HEADER, "safe") == "fast": + fast = True + mode = black.FileMode( + target_versions=versions, + is_pyi=pyi, + line_length=line_length, + skip_source_first_line=skip_source_first_line, + string_normalization=not skip_string_normalization, + magic_trailing_comma=not skip_magic_trailing_comma, + preview=preview, + ) + req_bytes = await request.content.read() + charset = request.charset if request.charset is not None else "utf8" + req_str = req_bytes.decode(charset) + then = datetime.utcnow() + + header = "" + if skip_source_first_line: + first_newline_position: int = req_str.find("\n") + 1 + header = req_str[:first_newline_position] + req_str = req_str[first_newline_position:] + + loop = asyncio.get_event_loop() + formatted_str = await loop.run_in_executor( + executor, partial(black.format_file_contents, req_str, fast=fast, mode=mode) + ) + + # Preserve CRLF line endings + if req_str[req_str.find("\n") - 1] == "\r": + formatted_str = formatted_str.replace("\n", "\r\n") + # If, after swapping line endings, nothing changed, then say so + if formatted_str == req_str: + raise black.NothingChanged + + # Put the source first line back + req_str = header + req_str + formatted_str = header + formatted_str + + # Only output the diff in the HTTP response + only_diff = bool(request.headers.get(DIFF_HEADER, False)) + if only_diff: + now = datetime.utcnow() + src_name = f"In\t{then} +0000" + dst_name = f"Out\t{now} +0000" + loop = asyncio.get_event_loop() + formatted_str = await loop.run_in_executor( + executor, + partial(black.diff, req_str, formatted_str, src_name, dst_name), + ) + + return web.Response( + content_type=request.content_type, + charset=charset, + headers=headers, + text=formatted_str, + ) + except black.NothingChanged: + return web.Response(status=204, headers=headers) + except black.InvalidInput as e: + return web.Response(status=400, headers=headers, text=str(e)) + except Exception as e: + logging.exception("Exception during handling a request") + return web.Response(status=500, headers=headers, text=str(e)) + + +def parse_python_variant_header(value: str) -> Tuple[bool, Set[black.TargetVersion]]: + if value == "pyi": + return True, set() + else: + versions = set() + for version in value.split(","): + if version.startswith("py"): + version = version[len("py") :] + if "." in version: + major_str, *rest = version.split(".") + else: + major_str = version[0] + rest = [version[1:]] if len(version) > 1 else [] + try: + major = int(major_str) + if major not in (2, 3): + raise InvalidVariantHeader("major version must be 2 or 3") + if len(rest) > 0: + minor = int(rest[0]) + if major == 2: + raise InvalidVariantHeader("Python 2 is not supported") + else: + # Default to lowest supported minor version. + minor = 7 if major == 2 else 3 + version_str = f"PY{major}{minor}" + if major == 3 and not hasattr(black.TargetVersion, version_str): + raise InvalidVariantHeader(f"3.{minor} is not supported") + versions.add(black.TargetVersion[version_str]) + except (KeyError, ValueError): + raise InvalidVariantHeader("expected e.g. '3.7', 'py3.5'") from None + return False, versions + + +def patched_main() -> None: + maybe_install_uvloop() + freeze_support() + black.patch_click() + main() + + +if __name__ == "__main__": + patched_main() diff --git a/.venv/lib/python3.11/site-packages/blackd/__main__.py b/.venv/lib/python3.11/site-packages/blackd/__main__.py new file mode 100644 index 00000000..b5a4b137 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/blackd/__main__.py @@ -0,0 +1,3 @@ +import blackd + +blackd.patched_main() diff --git a/.venv/lib/python3.11/site-packages/blackd/middlewares.py b/.venv/lib/python3.11/site-packages/blackd/middlewares.py new file mode 100644 index 00000000..370e0ae2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/blackd/middlewares.py @@ -0,0 +1,45 @@ +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Iterable, TypeVar + +from aiohttp.web_request import Request +from aiohttp.web_response import StreamResponse + +if TYPE_CHECKING: + F = TypeVar("F", bound=Callable[..., Any]) + middleware: Callable[[F], F] +else: + try: + from aiohttp.web_middlewares import middleware + except ImportError: + # @middleware is deprecated and its behaviour is the default since aiohttp 4.0 + # so if it doesn't exist anymore, define a no-op for forward compatibility. + middleware = lambda x: x # noqa: E731 + +Handler = Callable[[Request], Awaitable[StreamResponse]] +Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]] + + +def cors(allow_headers: Iterable[str]) -> Middleware: + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + is_options = request.method == "OPTIONS" + is_preflight = is_options and "Access-Control-Request-Method" in request.headers + if is_preflight: + resp = StreamResponse() + else: + resp = await handler(request) + + origin = request.headers.get("Origin") + if not origin: + return resp + + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["Access-Control-Expose-Headers"] = "*" + if is_options: + resp.headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers) + resp.headers["Access-Control-Allow-Methods"] = ", ".join( + ("OPTIONS", "POST") + ) + + return resp + + return impl diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/LICENSE new file mode 100644 index 00000000..467c38e4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014-2017, Mozilla Foundation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/METADATA new file mode 100644 index 00000000..cf72d935 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/METADATA @@ -0,0 +1,1222 @@ +Metadata-Version: 2.1 +Name: bleach +Version: 6.0.0 +Summary: An easy safelist-based HTML-sanitizing tool. +Home-page: https://github.com/mozilla/bleach +Maintainer: Will Kahn-Greene +Maintainer-email: willkg@mozilla.com +License: Apache Software License +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: six (>=1.9.0) +Requires-Dist: webencodings +Provides-Extra: css +Requires-Dist: tinycss2 (<1.2,>=1.1.0) ; extra == 'css' + +====== +Bleach +====== + +.. image:: https://github.com/mozilla/bleach/workflows/Test/badge.svg + :target: https://github.com/mozilla/bleach/actions?query=workflow%3ATest + +.. image:: https://github.com/mozilla/bleach/workflows/Lint/badge.svg + :target: https://github.com/mozilla/bleach/actions?query=workflow%3ALint + +.. image:: https://badge.fury.io/py/bleach.svg + :target: http://badge.fury.io/py/bleach + +Bleach is an allowed-list-based HTML sanitizing library that escapes or strips +markup and attributes. + +Bleach can also linkify text safely, applying filters that Django's ``urlize`` +filter cannot, and optionally setting ``rel`` attributes, even on links already +in the text. + +Bleach is intended for sanitizing text from *untrusted* sources. If you find +yourself jumping through hoops to allow your site administrators to do lots of +things, you're probably outside the use cases. Either trust those users, or +don't. + +Because it relies on html5lib_, Bleach is as good as modern browsers at dealing +with weird, quirky HTML fragments. And *any* of Bleach's methods will fix +unbalanced or mis-nested tags. + +The version on GitHub_ is the most up-to-date and contains the latest bug +fixes. You can find full documentation on `ReadTheDocs`_. + +:Code: https://github.com/mozilla/bleach +:Documentation: https://bleach.readthedocs.io/ +:Issue tracker: https://github.com/mozilla/bleach/issues +:License: Apache License v2; see LICENSE file + + +Reporting Bugs +============== + +For regular bugs, please report them `in our issue tracker +`_. + +If you believe that you've found a security vulnerability, please `file a secure +bug report in our bug tracker +`_ +or send an email to *security AT mozilla DOT org*. + +For more information on security-related bug disclosure and the PGP key to use +for sending encrypted mail or to verify responses received from that address, +please read our wiki page at +``_. + + +Security +======== + +Bleach is a security-focused library. + +We have a responsible security vulnerability reporting process. Please use +that if you're reporting a security issue. + +Security issues are fixed in private. After we land such a fix, we'll do a +release. + +For every release, we mark security issues we've fixed in the ``CHANGES`` in +the **Security issues** section. We include any relevant CVE links. + + +Installing Bleach +================= + +Bleach is available on PyPI_, so you can install it with ``pip``:: + + $ pip install bleach + + +Upgrading Bleach +================ + +.. warning:: + + Before doing any upgrades, read through `Bleach Changes + `_ for backwards + incompatible changes, newer versions, etc. + + Bleach follows `semver 2`_ versioning. Vendored libraries will not + be changed in patch releases. + + +Basic use +========= + +The simplest way to use Bleach is: + +.. code-block:: python + + >>> import bleach + + >>> bleach.clean('an example') + u'an <script>evil()</script> example' + + >>> bleach.linkify('an http://example.com url') + u'an http://example.com url' + + +Code of Conduct +=============== + +This project and repository is governed by Mozilla's code of conduct and +etiquette guidelines. For more details please see the `CODE_OF_CONDUCT.md +`_ + + +.. _html5lib: https://github.com/html5lib/html5lib-python +.. _GitHub: https://github.com/mozilla/bleach +.. _ReadTheDocs: https://bleach.readthedocs.io/ +.. _PyPI: https://pypi.org/project/bleach/ +.. _semver 2: https://semver.org/ + + +Bleach changes +============== + +Version 6.0.0 (January 23rd, 2023) +---------------------------------- + +**Backwards incompatible changes** + +* ``bleach.clean``, ``bleach.sanitizer.Cleaner``, + ``bleach.html5lib_shim.BleachHTMLParser``: the ``tags`` and ``protocols`` + arguments were changed from lists to sets. + + Old pre-6.0.0: + + .. code-block:: python + + bleach.clean( + "some text", + tags=["a", "p", "img"], + # ^ ^ list + protocols=["http", "https"], + # ^ ^ list + ) + + + New 6.0.0 and later: + + .. code-block:: python + + bleach.clean( + "some text", + tags={"a", "p", "img"}, + # ^ ^ set + protocols={"http", "https"}, + # ^ ^ set + ) + +* ``bleach.linkify``, ``bleach.linkifier.Linker``: the ``skip_tags`` and + ``recognized_tags`` arguments were changed from lists to sets. + + Old pre-6.0.0: + + .. code-block:: python + + bleach.linkify( + "some text", + skip_tags=["pre"], + # ^ ^ list + ) + + linker = Linker( + skip_tags=["pre"], + # ^ ^ list + recognized_tags=html5lib_shim.HTML_TAGS + ["custom-element"], + # ^ ^ ^ list + # | + # | list concatenation + ) + + New 6.0.0 and later: + + .. code-block:: python + + bleach.linkify( + "some text", + skip_tags={"pre"}, + # ^ ^ set + ) + + linker = Linker( + skip_tags={"pre"}, + # ^ ^ set + recognized_tags=html5lib_shim.HTML_TAGS | {"custom-element"}, + # ^ ^ ^ set + # | + # | union operator + ) + +* ``bleach.sanitizer.BleachSanitizerFilter``: ``strip_allowed_elements`` is now + ``strip_allowed_tags``. We now use "tags" everywhere rather than a mishmash + of "tags" in some places and "elements" in others. + + +**Security fixes** + +None + + +**Bug fixes** + +* Add support for Python 3.11. (#675) + +* Fix API weirness in ``BleachSanitizerFilter``. (#649) + + We're using "tags" instead of "elements" everywhere--no more weird + overloading of "elements" anymore. + + Also, it no longer calls the superclass constructor. + +* Add warning when ``css_sanitizer`` isn't set, but the ``style`` + attribute is allowed. (#676) + +* Fix linkify handling of character entities. (#501) + +* Rework dev dependencies to use ``requirements-dev.txt`` and + ``requirements-flake8.txt`` instead of extras. + +* Fix project infrastructure to be tox-based so it's easier to have CI + run the same things we're running in development and with flake8 + in an isolated environment. + +* Update action versions in CI. + +* Switch to f-strings where possible. Make tests parametrized to be + easier to read/maintain. + + +Version 5.0.1 (June 27th, 2022) +------------------------------- + +**Security fixes** + +None + + +**Bug fixes** + +* Add missing comma to tinycss2 require. Thank you, @shadchin! + +* Add url parse tests based on wpt url tests. (#688) + +* Support scheme-less urls if "https" is in allow list. (#662) + +* Handle escaping ``<`` in edge cases where it doesn't start a tag. (#544) + +* Fix reference warnings in docs. (#660) + +* Correctly urlencode email address parts. Thank you, @larseggert! (#659) + + +Version 5.0.0 (April 7th, 2022) +------------------------------- + +**Backwards incompatible changes** + +* ``clean`` and ``linkify`` now preserve the order of HTML attributes. Thank + you, @askoretskly! (#566) + +* Drop support for Python 3.6. Thank you, @hugovk! (#629) + +* CSS sanitization in style tags is completely different now. If you're using + Bleach ``clean`` to sanitize css in style tags, you'll need to update your + code and you'll need to install the ``css`` extras:: + + pip install 'bleach[css]' + + See `the documentation on sanitizing CSS for how to do it + `_. (#633) + +**Security fixes** + +None + +**Bug fixes** + +* Rework dev dependencies. We no longer have + ``requirements-dev.in``/``requirements-dev.txt``. Instead, we're using + ``dev`` extras. + + See `development docs `_ + for more details. (#620) + +* Add newline when dropping block-level tags. Thank you, @jvanasco! (#369) + + +Version 4.1.0 (August 25th, 2021) +--------------------------------- + +**Features** + +* Python 3.9 support + +**Security fixes** + +None + +**Bug fixes** + +* Update sanitizer clean to use vendored 3.6.14 stdlib urllib.parse to + fix test failures on Python 3.9. (#536) + + +Version 4.0.0 (August 3rd, 2021) +-------------------------------- + +**Backwards incompatible changes** + +* Drop support for unsupported Python versions <3.6. (#520) + +**Security fixes** + +None + +**Features** + +* fix attribute name in the linkify docs (thanks @CheesyFeet!) + + +Version 3.3.1 (July 14th, 2021) +------------------------------- + +**Security fixes** + +None + +**Features** + +* add more tests for CVE-2021-23980 / GHSA-vv2x-vrpj-qqpq +* bump python version to 3.8 for tox doc, vendorverify, and lint targets +* update bug report template tag +* update vendorverify script to detect and fail when extra files are vendored +* update release process docs to check vendorverify passes locally + +**Bug fixes** + +* remove extra vendored django present in the v3.3.0 whl (#595) +* duplicate h1 header doc fix (thanks Nguyễn Gia Phong / @McSinyx!) + + +Version 3.3.0 (February 1st, 2021) +---------------------------------- + +**Backwards incompatible changes** + +* clean escapes HTML comments even when strip_comments=False + +**Security fixes** + +* Fix bug 1621692 / GHSA-m6xf-fq7q-8743. See the advisory for details. + +**Features** + +None + +**Bug fixes** + +None + + +Version 3.2.3 (January 26th, 2021) +---------------------------------- + +**Security fixes** + +None + +**Features** + +None + +**Bug fixes** + +* fix clean and linkify raising ValueErrors for certain inputs. Thank you @Google-Autofuzz. + + +Version 3.2.2 (January 20th, 2021) +---------------------------------- + +**Security fixes** + +None + +**Features** + +* Migrate CI to Github Actions. Thank you @hugovk. + +**Bug fixes** + +* fix linkify raising an IndexError on certain inputs. Thank you @Google-Autofuzz. + + +Version 3.2.1 (September 18th, 2020) +------------------------------------ + +**Security fixes** + +None + +**Features** + +None + +**Bug fixes** + +* change linkifier to add rel="nofollow" as documented. Thank you @mitar. +* suppress html5lib sanitizer DeprecationWarnings (#557) + + +Version 3.2.0 (September 16th, 2020) +------------------------------------ + +**Security fixes** + +None + +**Features** + +None + +**Bug fixes** + +* ``html5lib`` dependency to version 1.1.0. Thank you Sam Sneddon. +* update tests_website terminology. Thank you Thomas Grainger. + + +Version 3.1.5 (April 29th, 2020) +-------------------------------- + +**Security fixes** + +None + +**Features** + +None + +**Bug fixes** + +* replace missing ``setuptools`` dependency with ``packaging``. Thank you Benjamin Peterson. + + +Version 3.1.4 (March 24th, 2020) +-------------------------------- + +**Security fixes** + +* ``bleach.clean`` behavior parsing style attributes could result in a + regular expression denial of service (ReDoS). + + Calls to ``bleach.clean`` with an allowed tag with an allowed + ``style`` attribute were vulnerable to ReDoS. For example, + ``bleach.clean(..., attributes={'a': ['style']})``. + + This issue was confirmed in Bleach versions v3.1.3, v3.1.2, v3.1.1, + v3.1.0, v3.0.0, v2.1.4, and v2.1.3. Earlier versions used a similar + regular expression and should be considered vulnerable too. + + Anyone using Bleach <=v3.1.3 is encouraged to upgrade. + + https://bugzilla.mozilla.org/show_bug.cgi?id=1623633 + +**Backwards incompatible changes** + +* Style attributes with dashes, or single or double quoted values are + cleaned instead of passed through. + +**Features** + +None + +**Bug fixes** + +None + + +Version 3.1.3 (March 17th, 2020) +-------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +* Drop support for Python 3.4. Thank you, @hugovk! + +* Drop deprecated ``setup.py test`` support. Thank you, @jdufresne! (#507) + +**Features** + +* Add support for Python 3.8. Thank you, @jdufresne! + +* Add support for PyPy 7. Thank you, @hugovk! + +* Add pypy3 testing to tox and travis. Thank you, @jdufresne! + +**Bug fixes** + +* Add relative link to code of conduct. (#442) + +* Fix typo: curren -> current in tests/test_clean.py Thank you, timgates42! (#504) + +* Fix handling of non-ascii style attributes. Thank you, @sekineh! (#426) + +* Simplify tox configuration. Thank you, @jdufresne! + +* Make documentation reproducible. Thank you, @lamby! + +* Fix typos in code comments. Thank you, @zborboa-g! + +* Fix exception value testing. Thank you, @mastizada! + +* Fix parser-tags NoneType exception. Thank you, @bope! + +* Improve TLD support in linkify. Thank you, @pc-coholic! + + +Version 3.1.2 (March 11th, 2020) +-------------------------------- + +**Security fixes** + +* ``bleach.clean`` behavior parsing embedded MathML and SVG content + with RCDATA tags did not match browser behavior and could result in + a mutation XSS. + + Calls to ``bleach.clean`` with ``strip=False`` and ``math`` or + ``svg`` tags and one or more of the RCDATA tags ``script``, + ``noscript``, ``style``, ``noframes``, ``iframe``, ``noembed``, or + ``xmp`` in the allowed tags whitelist were vulnerable to a mutation + XSS. + + This security issue was confirmed in Bleach version v3.1.1. Earlier + versions are likely affected too. + + Anyone using Bleach <=v3.1.1 is encouraged to upgrade. + + https://bugzilla.mozilla.org/show_bug.cgi?id=1621692 + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +None + + +Version 3.1.1 (February 13th, 2020) +----------------------------------- + +**Security fixes** + +* ``bleach.clean`` behavior parsing ``noscript`` tags did not match + browser behavior. + + Calls to ``bleach.clean`` allowing ``noscript`` and one or more of + the raw text tags (``title``, ``textarea``, ``script``, ``style``, + ``noembed``, ``noframes``, ``iframe``, and ``xmp``) were vulnerable + to a mutation XSS. + + This security issue was confirmed in Bleach versions v2.1.4, v3.0.2, + and v3.1.0. Earlier versions are probably affected too. + + Anyone using Bleach <=v3.1.0 is highly encouraged to upgrade. + + https://bugzilla.mozilla.org/show_bug.cgi?id=1615315 + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +None + + +Version 3.1.0 (January 9th, 2019) +--------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +None + +**Features** + +* Add ``recognized_tags`` argument to the linkify ``Linker`` class. This + fixes issues when linkifying on its own and having some tags get escaped. + It defaults to a list of HTML5 tags. Thank you, Chad Birch! (#409) + +**Bug fixes** + +* Add ``six>=1.9`` to requirements. Thank you, Dave Shawley (#416) + +* Fix cases where attribute names could have invalid characters in them. + (#419) + +* Fix problems with ``LinkifyFilter`` not being able to match links + across ``&``. (#422) + +* Fix ``InputStreamWithMemory`` when the ``BleachHTMLParser`` is + parsing ``meta`` tags. (#431) + +* Fix doctests. (#357) + + +Version 3.0.2 (October 11th, 2018) +---------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +* Merge ``Characters`` tokens after sanitizing them. This fixes issues in the + ``LinkifyFilter`` where it was only linkifying parts of urls. (#374) + + +Version 3.0.1 (October 9th, 2018) +--------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +None + +**Features** + +* Support Python 3.7. It supported Python 3.7 just fine, but we added 3.7 to + the list of Python environments we test so this is now officially supported. + (#377) + +**Bug fixes** + +* Fix ``list`` object has no attribute ``lower`` in ``clean``. (#398) +* Fix ``abbr`` getting escaped in ``linkify``. (#400) + + +Version 3.0.0 (October 3rd, 2018) +--------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +* A bunch of functions were moved from one module to another. + + These were moved from ``bleach.sanitizer`` to ``bleach.html5lib_shim``: + + * ``convert_entity`` + * ``convert_entities`` + * ``match_entity`` + * ``next_possible_entity`` + * ``BleachHTMLSerializer`` + * ``BleachHTMLTokenizer`` + * ``BleachHTMLParser`` + + These functions and classes weren't documented and aren't part of the + public API, but people read code and might be using them so we're + considering it an incompatible API change. + + If you're using them, you'll need to update your code. + +**Features** + +* Bleach no longer depends on html5lib. html5lib==1.0.1 is now vendored into + Bleach. You can remove it from your requirements file if none of your other + requirements require html5lib. + + This means Bleach will now work fine with other libraries that depend on + html5lib regardless of what version of html5lib they require. (#386) + +**Bug fixes** + +* Fixed tags getting added when using clean or linkify. This was a + long-standing regression from the Bleach 2.0 rewrite. (#280, #392) + +* Fixed ```` getting replaced with a string. Now it gets escaped or + stripped depending on whether it's in the allowed tags or not. (#279) + + +Version 2.1.4 (August 16th, 2018) +--------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +* Dropped support for Python 3.3. (#328) + +**Features** + +None + +**Bug fixes** + +* Handle ambiguous ampersands in correctly. (#359) + + +Version 2.1.3 (March 5th, 2018) +------------------------------- + +**Security fixes** + +* Attributes that have URI values weren't properly sanitized if the + values contained character entities. Using character entities, it + was possible to construct a URI value with a scheme that was not + allowed that would slide through unsanitized. + + This security issue was introduced in Bleach 2.1. Anyone using + Bleach 2.1 is highly encouraged to upgrade. + + https://bugzilla.mozilla.org/show_bug.cgi?id=1442745 + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +* Fixed some other edge cases for attribute URI value sanitizing and + improved testing of this code. + + +Version 2.1.2 (December 7th, 2017) +---------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +* Support html5lib-python 1.0.1. (#337) + +* Add deprecation warning for supporting html5lib-python < 1.0. + +* Switch to semver. + + +Version 2.1.1 (October 2nd, 2017) +--------------------------------- + +**Security fixes** + +None + +**Backwards incompatible changes** + +None + +**Features** + +None + +**Bug fixes** + +* Fix ``setup.py`` opening files when ``LANG=``. (#324) + + +Version 2.1 (September 28th, 2017) +---------------------------------- + +**Security fixes** + +* Convert control characters (backspace particularly) to "?" preventing + malicious copy-and-paste situations. (#298) + + See ``_ for more details. + + This affects all previous versions of Bleach. Check the comments on that + issue for ways to alleviate the issue if you can't upgrade to Bleach 2.1. + + +**Backwards incompatible changes** + +* Redid versioning. ``bleach.VERSION`` is no longer available. Use the string + version at ``bleach.__version__`` and parse it with + ``pkg_resources.parse_version``. (#307) + +* clean, linkify: linkify and clean should only accept text types; thank you, + Janusz! (#292) + +* clean, linkify: accept only unicode or utf-8-encoded str (#176) + + +**Features** + + +**Bug fixes** + +* ``bleach.clean()`` no longer unescapes entities including ones that are missing + a ``;`` at the end which can happen in urls and other places. (#143) + +* linkify: fix http links inside of mailto links; thank you, sedrubal! (#300) + +* clarify security policy in docs (#303) + +* fix dependency specification for html5lib 1.0b8, 1.0b9, and 1.0b10; thank you, + Zoltán! (#268) + +* add Bleach vs. html5lib comparison to README; thank you, Stu Cox! (#278) + +* fix KeyError exceptions on tags without href attr; thank you, Alex Defsen! + (#273) + +* add test website and scripts to test ``bleach.clean()`` output in browser; + thank you, Greg Guthe! + + +Version 2.0 (March 8th, 2017) +----------------------------- + +**Security fixes** + +* None + + +**Backwards incompatible changes** + +* Removed support for Python 2.6. (#206) + +* Removed support for Python 3.2. (#224) + +* Bleach no longer supports html5lib < 0.99999999 (8 9s). + + This version is a rewrite to use the new sanitizing API since the old + one was dropped in html5lib 0.99999999 (8 9s). + + If you're using 0.9999999 (7 9s) upgrade to 0.99999999 (8 9s) or higher. + + If you're using 1.0b8 (equivalent to 0.9999999 (7 9s)), upgrade to 1.0b9 + (equivalent to 0.99999999 (8 9s)) or higher. + +* ``bleach.clean`` and friends were rewritten + + ``clean`` was reimplemented as an html5lib filter and happens at a different + step in the HTML parsing -> traversing -> serializing process. Because of + that, there are some differences in clean's output as compared with previous + versions. + + Amongst other things, this version will add end tags even if the tag in + question is to be escaped. + +* ``bleach.clean`` and friends attribute callables now take three arguments: + tag, attribute name and attribute value. Previously they only took attribute + name and attribute value. + + All attribute callables will need to be updated. + +* ``bleach.linkify`` was rewritten + + ``linkify`` was reimplemented as an html5lib Filter. As such, it no longer + accepts a ``tokenizer`` argument. + + The callback functions for adjusting link attributes now takes a namespaced + attribute. + + Previously you'd do something like this:: + + def check_protocol(attrs, is_new): + if not attrs.get('href', '').startswith('http:', 'https:')): + return None + return attrs + + Now it's more like this:: + + def check_protocol(attrs, is_new): + if not attrs.get((None, u'href'), u'').startswith(('http:', 'https:')): + # ^^^^^^^^^^^^^^^ + return None + return attrs + + Further, you need to make sure you're always using unicode values. If you + don't then html5lib will raise an assertion error that the value is not + unicode. + + All linkify filters will need to be updated. + +* ``bleach.linkify`` and friends had a ``skip_pre`` argument--that's been + replaced with a more general ``skip_tags`` argument. + + Before, you might do:: + + bleach.linkify(some_text, skip_pre=True) + + The equivalent with Bleach 2.0 is:: + + bleach.linkify(some_text, skip_tags=['pre']) + + You can skip other tags, too, like ``style`` or ``script`` or other places + where you don't want linkification happening. + + All uses of linkify that use ``skip_pre`` will need to be updated. + + +**Changes** + +* Supports Python 3.6. + +* Supports html5lib >= 0.99999999 (8 9s). + +* There's a ``bleach.sanitizer.Cleaner`` class that you can instantiate with your + favorite clean settings for easy reuse. + +* There's a ``bleach.linkifier.Linker`` class that you can instantiate with your + favorite linkify settings for easy reuse. + +* There's a ``bleach.linkifier.LinkifyFilter`` which is an htm5lib filter that + you can pass as a filter to ``bleach.sanitizer.Cleaner`` allowing you to clean + and linkify in one pass. + +* ``bleach.clean`` and friends can now take a callable as an attributes arg value. + +* Tons of bug fixes. + +* Cleaned up tests. + +* Documentation fixes. + + +Version 1.5 (November 4th, 2016) +-------------------------------- + +**Security fixes** + +* None + +**Backwards incompatible changes** + +* clean: The list of ``ALLOWED_PROTOCOLS`` now defaults to http, https and + mailto. + + Previously it was a long list of protocols something like ed2k, ftp, http, + https, irc, mailto, news, gopher, nntp, telnet, webcal, xmpp, callto, feed, + urn, aim, rsync, tag, ssh, sftp, rtsp, afs, data. (#149) + +**Changes** + +* clean: Added ``protocols`` to arguments list to let you override the list of + allowed protocols. Thank you, Andreas Malecki! (#149) + +* linkify: Fix a bug involving periods at the end of an email address. Thank you, + Lorenz Schori! (#219) + +* linkify: Fix linkification of non-ascii ports. Thank you Alexandre, Macabies! + (#207) + +* linkify: Fix linkify inappropriately removing node tails when dropping nodes. + (#132) + +* Fixed a test that failed periodically. (#161) + +* Switched from nose to py.test. (#204) + +* Add test matrix for all supported Python and html5lib versions. (#230) + +* Limit to html5lib ``>=0.999,!=0.9999,!=0.99999,<0.99999999`` because 0.9999 + and 0.99999 are busted. + +* Add support for ``python setup.py test``. (#97) + + +Version 1.4.3 (May 23rd, 2016) +------------------------------ + +**Security fixes** + +* None + +**Changes** + +* Limit to html5lib ``>=0.999,<0.99999999`` because of impending change to + sanitizer api. #195 + + +Version 1.4.2 (September 11, 2015) +---------------------------------- + +**Changes** + +* linkify: Fix hang in linkify with ``parse_email=True``. (#124) + +* linkify: Fix crash in linkify when removing a link that is a first-child. (#136) + +* Updated TLDs. + +* linkify: Don't remove exterior brackets when linkifying. (#146) + + +Version 1.4.1 (December 15, 2014) +--------------------------------- + +**Changes** + +* Consistent order of attributes in output. + +* Python 3.4 support. + + +Version 1.4 (January 12, 2014) +------------------------------ + +**Changes** + +* linkify: Update linkify to use etree type Treewalker instead of simpletree. + +* Updated html5lib to version ``>=0.999``. + +* Update all code to be compatible with Python 3 and 2 using six. + +* Switch to Apache License. + + +Version 1.3 +----------- + +* Used by Python 3-only fork. + + +Version 1.2.2 (May 18, 2013) +---------------------------- + +* Pin html5lib to version 0.95 for now due to major API break. + + +Version 1.2.1 (February 19, 2013) +--------------------------------- + +* ``clean()`` no longer considers ``feed:`` an acceptable protocol due to + inconsistencies in browser behavior. + + +Version 1.2 (January 28, 2013) +------------------------------ + +* ``linkify()`` has changed considerably. Many keyword arguments have been + replaced with a single callbacks list. Please see the documentation for more + information. + +* Bleach will no longer consider unacceptable protocols when linkifying. + +* ``linkify()`` now takes a tokenizer argument that allows it to skip + sanitization. + +* ``delinkify()`` is gone. + +* Removed exception handling from ``_render``. ``clean()`` and ``linkify()`` may + now throw. + +* ``linkify()`` correctly ignores case for protocols and domain names. + +* ``linkify()`` correctly handles markup within an tag. + + +Version 1.1.5 +------------- + + +Version 1.1.4 +------------- + + +Version 1.1.3 (July 10, 2012) +----------------------------- + +* Fix parsing bare URLs when parse_email=True. + + +Version 1.1.2 (June 1, 2012) +---------------------------- + +* Fix hang in style attribute sanitizer. (#61) + +* Allow ``/`` in style attribute values. + + +Version 1.1.1 (February 17, 2012) +--------------------------------- + +* Fix tokenizer for html5lib 0.9.5. + + +Version 1.1.0 (October 24, 2011) +-------------------------------- + +* ``linkify()`` now understands port numbers. (#38) + +* Documented character encoding behavior. (#41) + +* Add an optional target argument to ``linkify()``. + +* Add ``delinkify()`` method. (#45) + +* Support subdomain whitelist for ``delinkify()``. (#47, #48) + + +Version 1.0.4 (September 2, 2011) +--------------------------------- + +* Switch to SemVer git tags. + +* Make ``linkify()`` smarter about trailing punctuation. (#30) + +* Pass ``exc_info`` to logger during rendering issues. + +* Add wildcard key for attributes. (#19) + +* Make ``linkify()`` use the ``HTMLSanitizer`` tokenizer. (#36) + +* Fix URLs wrapped in parentheses. (#23) + +* Make ``linkify()`` UTF-8 safe. (#33) + + +Version 1.0.3 (June 14, 2011) +----------------------------- + +* ``linkify()`` works with 3rd level domains. (#24) + +* ``clean()`` supports vendor prefixes in style values. (#31, #32) + +* Fix ``linkify()`` email escaping. + + +Version 1.0.2 (June 6, 2011) +---------------------------- + +* ``linkify()`` supports email addresses. + +* ``clean()`` supports callables in attributes filter. + + +Version 1.0.1 (April 12, 2011) +------------------------------ + +* ``linkify()`` doesn't drop trailing slashes. (#21) +* ``linkify()`` won't linkify 'libgl.so.1'. (#22) diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/RECORD new file mode 100644 index 00000000..3b936308 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/RECORD @@ -0,0 +1,103 @@ +bleach-6.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bleach-6.0.0.dist-info/LICENSE,sha256=vsIjjBSaYyuPsmgT9oes6rq4AyfzJwdpwsFhV4g9MTA,569 +bleach-6.0.0.dist-info/METADATA,sha256=9rTe871FE18onbZcdlZFtTfPai2HySOuYtDZYd3f09o,29799 +bleach-6.0.0.dist-info/RECORD,, +bleach-6.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach-6.0.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +bleach-6.0.0.dist-info/top_level.txt,sha256=dcv0wKIySB0zMjAEXLwY4V0-3IN9UZQGAT1wDmfQICY,7 +bleach/__init__.py,sha256=4BQh_aKiLYDvtix_nDA4r66fn2HQOeRirhNqxYaZIL0,3649 +bleach/__pycache__/__init__.cpython-311.pyc,, +bleach/__pycache__/callbacks.cpython-311.pyc,, +bleach/__pycache__/css_sanitizer.cpython-311.pyc,, +bleach/__pycache__/html5lib_shim.cpython-311.pyc,, +bleach/__pycache__/linkifier.cpython-311.pyc,, +bleach/__pycache__/parse_shim.cpython-311.pyc,, +bleach/__pycache__/sanitizer.cpython-311.pyc,, +bleach/_vendor/README.rst,sha256=eXeKT2JdZB4WX1kuhTa8W9Jp9VXtwIKFxo5RUL5exmM,2160 +bleach/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach/_vendor/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/__pycache__/parse.cpython-311.pyc,, +bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst,sha256=DrNAMifoDpuQyJn-KW-H6K8Tt2a5rKnV2UF4-DRrGUI,983 +bleach/_vendor/html5lib-1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bleach/_vendor/html5lib-1.1.dist-info/LICENSE,sha256=FqOZkWGekvGGgJMtoqkZn999ld8-yu3FLqBiGKq6_W8,1084 +bleach/_vendor/html5lib-1.1.dist-info/METADATA,sha256=Y3w-nd_22HQnQRy3yypVsV_ke2FF94uUD4-vGpc2DnI,16076 +bleach/_vendor/html5lib-1.1.dist-info/RECORD,sha256=u-y_W5lhdsHC1OSMnA4bCi3-11IgQ_FAIW6viMu8_LA,3486 +bleach/_vendor/html5lib-1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach/_vendor/html5lib-1.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +bleach/_vendor/html5lib-1.1.dist-info/top_level.txt,sha256=XEX6CHpskSmvjJB4tP6m4Q5NYXhIf_0ceMc0PNbzJPQ,9 +bleach/_vendor/html5lib/__init__.py,sha256=pWnYcfZ69wNLrdQL7bpr49FUi8O8w0KhKCOHsyRgYGQ,1143 +bleach/_vendor/html5lib/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/_ihatexml.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/_inputstream.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/_tokenizer.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/_utils.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/constants.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/html5parser.cpython-311.pyc,, +bleach/_vendor/html5lib/__pycache__/serializer.cpython-311.pyc,, +bleach/_vendor/html5lib/_ihatexml.py,sha256=ifOwF7pXqmyThIXc3boWc96s4MDezqRrRVp7FwDYUFs,16728 +bleach/_vendor/html5lib/_inputstream.py,sha256=IKuMiY8rzb7pqIGCpbvTqsxysLEpgEHWYvYEFu4LUAI,32300 +bleach/_vendor/html5lib/_tokenizer.py,sha256=WvJQa2Mli4NtTmhLXkX8Jy5FcWttqCaiDTiKyaw8D-k,77028 +bleach/_vendor/html5lib/_trie/__init__.py,sha256=nqfgO910329BEVJ5T4psVwQtjd2iJyEXQ2-X8c1YxwU,109 +bleach/_vendor/html5lib/_trie/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/_trie/__pycache__/_base.cpython-311.pyc,, +bleach/_vendor/html5lib/_trie/__pycache__/py.cpython-311.pyc,, +bleach/_vendor/html5lib/_trie/_base.py,sha256=CaybYyMro8uERQYjby2tTeSUatnWDfWroUN9N7ety5w,1013 +bleach/_vendor/html5lib/_trie/py.py,sha256=zg7RZSHxJ8mLmuI_7VEIV8AomISrgkvqCP477AgXaG0,1763 +bleach/_vendor/html5lib/_utils.py,sha256=AxAJSG15eyarCgKMnlUwzs1X6jFHXqEvhlYEOxAFmis,4919 +bleach/_vendor/html5lib/constants.py,sha256=Ll-yzLU_jcjyAI_h57zkqZ7aQWE5t5xA4y_jQgoUUhw,83464 +bleach/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +bleach/_vendor/html5lib/filters/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/base.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/lint.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/__pycache__/whitespace.cpython-311.pyc,, +bleach/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=lViZc2JMCclXi_5gduvmdzrRxtO5Xo9ONnbHBVCsykU,919 +bleach/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 +bleach/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945 +bleach/_vendor/html5lib/filters/lint.py,sha256=upXATs6By7cot7o0bnNqR15sPq2Fn6Vnjvoy3gyO_rY,3631 +bleach/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588 +bleach/_vendor/html5lib/filters/sanitizer.py,sha256=XGNSdzIqDTaHot1V-rRj1V_XOolApJ7n95tHP9JcgNU,26885 +bleach/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214 +bleach/_vendor/html5lib/html5parser.py,sha256=w5hZJh0cvD3g4CS196DiTmuGpSKCMYe1GS46-yf_WZQ,117174 +bleach/_vendor/html5lib/serializer.py,sha256=K2kfoLyMPMFPfdusfR30SrxNkf0mJB92-P5_RntyaaI,15747 +bleach/_vendor/html5lib/treeadapters/__init__.py,sha256=18hyI-at2aBsdKzpwRwa5lGF1ipgctaTYXoU9En2ZQg,650 +bleach/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-311.pyc,, +bleach/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-311.pyc,, +bleach/_vendor/html5lib/treeadapters/genshi.py,sha256=CH27pAsDKmu4ZGkAUrwty7u0KauGLCZRLPMzaO3M5vo,1715 +bleach/_vendor/html5lib/treeadapters/sax.py,sha256=BKS8woQTnKiqeffHsxChUqL4q2ZR_wb5fc9MJ3zQC8s,1776 +bleach/_vendor/html5lib/treebuilders/__init__.py,sha256=AysSJyvPfikCMMsTVvaxwkgDieELD5dfR8FJIAuq7hY,3592 +bleach/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/base.cpython-311.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-311.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-311.pyc,, +bleach/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-311.pyc,, +bleach/_vendor/html5lib/treebuilders/base.py,sha256=oeZNGEB-kt90YJGVH05gb5a8E7ids2AbYwGRsVCieWk,14553 +bleach/_vendor/html5lib/treebuilders/dom.py,sha256=22whb0C71zXIsai5mamg6qzBEiigcBIvaDy4Asw3at0,8925 +bleach/_vendor/html5lib/treebuilders/etree.py,sha256=EbmHx-wQ-11MVucTPtF7Ul92-mQGN3Udu_KfDn-Ifhk,12824 +bleach/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=OazDHZGO_q4FnVs4Dhs4hzzn2JwGAOs-rfV8LAlUGW4,14754 +bleach/_vendor/html5lib/treewalkers/__init__.py,sha256=OBPtc1TU5mGyy18QDMxKEyYEz0wxFUUNj5v0-XgmYhY,5719 +bleach/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/base.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-311.pyc,, +bleach/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476 +bleach/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413 +bleach/_vendor/html5lib/treewalkers/etree.py,sha256=gkD4tfEfRWPsEGvgHHJxZmKZXUvBzVVGz3v5C_MIiOE,4539 +bleach/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=eLedbn6nPjlpebibsWVijey7WEpzDwxU3ubwUoudBuA,6345 +bleach/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309 +bleach/_vendor/parse.py,sha256=Rq-WbjO2JHrh1X2UWRFaPrRs2p-AnJ8U4FKrwv6NrLI,39023 +bleach/_vendor/parse.py.SHA256SUM,sha256=-AaiqN-9otw_X0vFjKkbKWFvkp68iLME92_wI-8-vm0,75 +bleach/_vendor/vendor.txt,sha256=6FFZyenumgWqnhLgbCa4yzL4HVNaSUDC2DHNyR5Fy6w,184 +bleach/_vendor/vendor_install.sh,sha256=x_Pn4dkfzPMJCZKwHHFxp0EAL5RsIfz-HSdTWHuI4yA,453 +bleach/callbacks.py,sha256=JNTGiM5_3bKsGltpR9ZYEz_C_b7-vfDlTTdQCirbdyc,752 +bleach/css_sanitizer.py,sha256=QFMxRKBUMSuNvYkVpB2WRBQO609eFbU-p9P_LhU6jtM,2526 +bleach/html5lib_shim.py,sha256=LRockXiQam4HK-Df-X7fjrW1OF63UIHgTNo7-tSrFeY,22779 +bleach/linkifier.py,sha256=k7sOk4WILQNoLVhDye_GAOzhe0ZiWXLIKKAk2UB4zvY,22350 +bleach/parse_shim.py,sha256=VDPOdBOKbuDEceKVvfoggcr6A332bkcq4Z8jMtOJlAQ,50 +bleach/sanitizer.py,sha256=JqDuTINOybpc_eHBzG_H7cnkHdFskZGbfsaBc-hDPH8,21934 diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/REQUESTED b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/WHEEL new file mode 100644 index 00000000..57e3d840 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/top_level.txt new file mode 100644 index 00000000..a02d6008 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach-6.0.0.dist-info/top_level.txt @@ -0,0 +1 @@ +bleach diff --git a/.venv/lib/python3.11/site-packages/bleach/__init__.py b/.venv/lib/python3.11/site-packages/bleach/__init__.py new file mode 100644 index 00000000..4e87eb80 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/__init__.py @@ -0,0 +1,125 @@ +from bleach.linkifier import ( + DEFAULT_CALLBACKS, + Linker, +) +from bleach.sanitizer import ( + ALLOWED_ATTRIBUTES, + ALLOWED_PROTOCOLS, + ALLOWED_TAGS, + Cleaner, +) + + +# yyyymmdd +__releasedate__ = "20230123" +# x.y.z or x.y.z.dev0 -- semver +__version__ = "6.0.0" + + +__all__ = ["clean", "linkify"] + + +def clean( + text, + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + protocols=ALLOWED_PROTOCOLS, + strip=False, + strip_comments=True, + css_sanitizer=None, +): + """Clean an HTML fragment of malicious content and return it + + This function is a security-focused function whose sole purpose is to + remove malicious content from a string such that it can be displayed as + content in a web page. + + This function is not designed to use to transform content to be used in + non-web-page contexts. + + Example:: + + import bleach + + better_text = bleach.clean(yucky_text) + + + .. Note:: + + If you're cleaning a lot of text and passing the same argument values or + you want more configurability, consider using a + :py:class:`bleach.sanitizer.Cleaner` instance. + + :arg str text: the text to clean + + :arg set tags: set of allowed tags; defaults to + ``bleach.sanitizer.ALLOWED_TAGS`` + + :arg dict attributes: allowed attributes; can be a callable, list or dict; + defaults to ``bleach.sanitizer.ALLOWED_ATTRIBUTES`` + + :arg list protocols: allowed list of protocols for links; defaults + to ``bleach.sanitizer.ALLOWED_PROTOCOLS`` + + :arg bool strip: whether or not to strip disallowed elements + + :arg bool strip_comments: whether or not to strip HTML comments + + :arg CSSSanitizer css_sanitizer: instance with a "sanitize_css" method for + sanitizing style attribute values and style text; defaults to None + + :returns: cleaned text as unicode + + """ + cleaner = Cleaner( + tags=tags, + attributes=attributes, + protocols=protocols, + strip=strip, + strip_comments=strip_comments, + css_sanitizer=css_sanitizer, + ) + return cleaner.clean(text) + + +def linkify(text, callbacks=DEFAULT_CALLBACKS, skip_tags=None, parse_email=False): + """Convert URL-like strings in an HTML fragment to links + + This function converts strings that look like URLs, domain names and email + addresses in text that may be an HTML fragment to links, while preserving: + + 1. links already in the string + 2. urls found in attributes + 3. email addresses + + linkify does a best-effort approach and tries to recover from bad + situations due to crazy text. + + .. Note:: + + If you're linking a lot of text and passing the same argument values or + you want more configurability, consider using a + :py:class:`bleach.linkifier.Linker` instance. + + .. Note:: + + If you have text that you want to clean and then linkify, consider using + the :py:class:`bleach.linkifier.LinkifyFilter` as a filter in the clean + pass. That way you're not parsing the HTML twice. + + :arg str text: the text to linkify + + :arg list callbacks: list of callbacks to run when adjusting tag attributes; + defaults to ``bleach.linkifier.DEFAULT_CALLBACKS`` + + :arg list skip_tags: list of tags that you don't want to linkify the + contents of; for example, you could set this to ``['pre']`` to skip + linkifying contents of ``pre`` tags + + :arg bool parse_email: whether or not to linkify email addresses + + :returns: linkified text as unicode + + """ + linker = Linker(callbacks=callbacks, skip_tags=skip_tags, parse_email=parse_email) + return linker.linkify(text) diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/README.rst b/.venv/lib/python3.11/site-packages/bleach/_vendor/README.rst new file mode 100644 index 00000000..e53aede0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/_vendor/README.rst @@ -0,0 +1,61 @@ +======================= +Vendored library policy +======================= + +To simplify Bleach development, we're now vendoring certain libraries that +we use. + +Vendored libraries must follow these rules: + +1. Vendored libraries must be pure Python--no compiling. +2. Source code for the libary is included in this directory. +3. License must be included in this repo and in the Bleach distribution. +4. Requirements of the library become requirements of Bleach. +5. No modifications to the library may be made. + + +Adding/Updating a vendored library +================================== + +Way to vendor a library or update a version: + +1. Update ``vendor.txt`` with the library, version, and hash. You can use + `hashin `_. +2. Remove all old files and directories of the old version. +3. Run ``pip_install_vendor.sh`` and check everything it produced in including + the ``.dist-info`` directory and contents. +4. Update the bleach minor version in the next release. + + +Reviewing a change involving a vendored library +=============================================== + +Way to verify a vendored library addition/update: + +1. Pull down the branch. +2. Delete all the old files and directories of the old version. +3. Run ``pip_install_vendor.sh``. +4. Run ``git diff`` and verify there are no changes. + + +NB: the current ``vendor.txt`` was generated with pip 20.2.3, which might be necessary to reproduce the dist-info + + +Removing/Unvendoring a vendored library +======================================= + +A vendored library might be removed for any of the following reasons: + +* it violates the vendoring policy (e.g. an incompatible license + change) +* a suitable replacement is found +* bleach has the resources to test and QA new bleach releases against + multiple versions of the previously vendored library + +To unvendor a library: + +1. Remove the library and its hashes from ``vendor.txt``. +2. Remove library files and directories from this directory. +3. Run ``install_vendor.sh`` and check the previously vendored library including + the ``.dist-info`` directory and contents is not installed. +4. Update the bleach minor version in the next release. diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/__init__.py b/.venv/lib/python3.11/site-packages/bleach/_vendor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst new file mode 100644 index 00000000..90401390 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/AUTHORS.rst @@ -0,0 +1,66 @@ +Credits +======= + +``html5lib`` is written and maintained by: + +- James Graham +- Sam Sneddon +- Łukasz Langa +- Will Kahn-Greene + + +Patches and suggestions +----------------------- +(In chronological order, by first commit:) + +- Anne van Kesteren +- Lachlan Hunt +- lantis63 +- Sam Ruby +- Thomas Broyer +- Tim Fletcher +- Mark Pilgrim +- Ryan King +- Philip Taylor +- Edward Z. Yang +- fantasai +- Philip Jägenstedt +- Ms2ger +- Mohammad Taha Jahangir +- Andy Wingo +- Andreas Madsack +- Karim Valiev +- Juan Carlos Garcia Segovia +- Mike West +- Marc DM +- Simon Sapin +- Michael[tm] Smith +- Ritwik Gupta +- Marc Abramowitz +- Tony Lopes +- lilbludevil +- Kevin +- Drew Hubl +- Austin Kumbera +- Jim Baker +- Jon Dufresne +- Donald Stufft +- Alex Gaynor +- Nik Nyby +- Jakub Wilk +- Sigmund Cherem +- Gabi Davar +- Florian Mounier +- neumond +- Vitalik Verhovodov +- Kovid Goyal +- Adam Chainz +- John Vandenberg +- Eric Amorde +- Benedikt Morbach +- Jonathan Vanasco +- Tom Most +- Ville Skyttä +- Hugo van Kemenade +- Mark Vasilkov + diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/LICENSE new file mode 100644 index 00000000..c87fa7a0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2006-2013 James Graham and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/METADATA b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/METADATA new file mode 100644 index 00000000..ee83c1f8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/bleach/_vendor/html5lib-1.1.dist-info/METADATA @@ -0,0 +1,552 @@ +Metadata-Version: 2.1 +Name: html5lib +Version: 1.1 +Summary: HTML parser based on the WHATWG HTML specification +Home-page: https://github.com/html5lib/html5lib-python +Maintainer: James Graham +Maintainer-email: james@hoppipolla.co.uk +License: MIT License +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Requires-Dist: six (>=1.9) +Requires-Dist: webencodings +Provides-Extra: all +Requires-Dist: genshi ; extra == 'all' +Requires-Dist: chardet (>=2.2) ; extra == 'all' +Requires-Dist: lxml ; (platform_python_implementation == 'CPython') and extra == 'all' +Provides-Extra: chardet +Requires-Dist: chardet (>=2.2) ; extra == 'chardet' +Provides-Extra: genshi +Requires-Dist: genshi ; extra == 'genshi' +Provides-Extra: lxml +Requires-Dist: lxml ; (platform_python_implementation == 'CPython') and extra == 'lxml' + +html5lib +======== + +.. image:: https://travis-ci.org/html5lib/html5lib-python.svg?branch=master + :target: https://travis-ci.org/html5lib/html5lib-python + + +html5lib is a pure-python library for parsing HTML. It is designed to +conform to the WHATWG HTML specification, as is implemented by all major +web browsers. + + +Usage +----- + +Simple usage follows this pattern: + +.. code-block:: python + + import html5lib + with open("mydocument.html", "rb") as f: + document = html5lib.parse(f) + +or: + +.. code-block:: python + + import html5lib + document = html5lib.parse("

    Hello World!") + +By default, the ``document`` will be an ``xml.etree`` element instance. +Whenever possible, html5lib chooses the accelerated ``ElementTree`` +implementation (i.e. ``xml.etree.cElementTree`` on Python 2.x). + +Two other tree types are supported: ``xml.dom.minidom`` and +``lxml.etree``. To use an alternative format, specify the name of +a treebuilder: + +.. code-block:: python + + import html5lib + with open("mydocument.html", "rb") as f: + lxml_etree_document = html5lib.parse(f, treebuilder="lxml") + +When using with ``urllib2`` (Python 2), the charset from HTTP should be +pass into html5lib as follows: + +.. code-block:: python + + from contextlib import closing + from urllib2 import urlopen + import html5lib + + with closing(urlopen("http://example.com/")) as f: + document = html5lib.parse(f, transport_encoding=f.info().getparam("charset")) + +When using with ``urllib.request`` (Python 3), the charset from HTTP +should be pass into html5lib as follows: + +.. code-block:: python + + from urllib.request import urlopen + import html5lib + + with urlopen("http://example.com/") as f: + document = html5lib.parse(f, transport_encoding=f.info().get_content_charset()) + +To have more control over the parser, create a parser object explicitly. +For instance, to make the parser raise exceptions on parse errors, use: + +.. code-block:: python + + import html5lib + with open("mydocument.html", "rb") as f: + parser = html5lib.HTMLParser(strict=True) + document = parser.parse(f) + +When you're instantiating parser objects explicitly, pass a treebuilder +class as the ``tree`` keyword argument to use an alternative document +format: + +.. code-block:: python + + import html5lib + parser = html5lib.HTMLParser(tree=html5lib.getTreeBuilder("dom")) + minidom_document = parser.parse("

    Hello World!") + +More documentation is available at https://html5lib.readthedocs.io/. + + +Installation +------------ + +html5lib works on CPython 2.7+, CPython 3.5+ and PyPy. To install: + +.. code-block:: bash + + $ pip install html5lib + +The goal is to support a (non-strict) superset of the versions that `pip +supports +`_. + +Optional Dependencies +--------------------- + +The following third-party libraries may be used for additional +functionality: + +- ``lxml`` is supported as a tree format (for both building and + walking) under CPython (but *not* PyPy where it is known to cause + segfaults); + +- ``genshi`` has a treewalker (but not builder); and + +- ``chardet`` can be used as a fallback when character encoding cannot + be determined. + + +Bugs +---- + +Please report any bugs on the `issue tracker +`_. + + +Tests +----- + +Unit tests require the ``pytest`` and ``mock`` libraries and can be +run using the ``py.test`` command in the root directory. + +Test data are contained in a separate `html5lib-tests +`_ repository and included +as a submodule, thus for git checkouts they must be initialized:: + + $ git submodule init + $ git submodule update + +If you have all compatible Python implementations available on your +system, you can run tests on all of them using the ``tox`` utility, +which can be found on PyPI. + + +Questions? +---------- + +There's a mailing list available for support on Google Groups, +`html5lib-discuss `_, +though you may get a quicker response asking on IRC in `#whatwg on +irc.freenode.net `_. + +Change Log +---------- + +1.1 +~~~ + +UNRELEASED + +Breaking changes: + +* Drop support for Python 3.3. (#358) +* Drop support for Python 3.4. (#421) + +Deprecations: + +* Deprecate the ``html5lib`` sanitizer (``html5lib.serialize(sanitize=True)`` and + ``html5lib.filters.sanitizer``). We recommend users migrate to `Bleach + `. Please let us know if Bleach doesn't suffice for your + use. (#443) + +Other changes: + +* Try to import from ``collections.abc`` to remove DeprecationWarning and ensure + ``html5lib`` keeps working in future Python versions. (#403) +* Drop optional ``datrie`` dependency. (#442) + + +1.0.1 +~~~~~ + +Released on December 7, 2017 + +Breaking changes: + +* Drop support for Python 2.6. (#330) (Thank you, Hugo, Will Kahn-Greene!) +* Remove ``utils/spider.py`` (#353) (Thank you, Jon Dufresne!) + +Features: + +* Improve documentation. (#300, #307) (Thank you, Jon Dufresne, Tom Most, + Will Kahn-Greene!) +* Add iframe seamless boolean attribute. (Thank you, Ritwik Gupta!) +* Add itemscope as a boolean attribute. (#194) (Thank you, Jonathan Vanasco!) +* Support Python 3.6. (#333) (Thank you, Jon Dufresne!) +* Add CI support for Windows using AppVeyor. (Thank you, John Vandenberg!) +* Improve testing and CI and add code coverage (#323, #334), (Thank you, Jon + Dufresne, John Vandenberg, Sam Sneddon, Will Kahn-Greene!) +* Semver-compliant version number. + +Bug fixes: + +* Add support for setuptools < 18.5 to support environment markers. (Thank you, + John Vandenberg!) +* Add explicit dependency for six >= 1.9. (Thank you, Eric Amorde!) +* Fix regexes to work with Python 3.7 regex adjustments. (#318, #379) (Thank + you, Benedikt Morbach, Ville Skyttä, Mark Vasilkov!) +* Fix alphabeticalattributes filter namespace bug. (#324) (Thank you, Will + Kahn-Greene!) +* Include license file in generated wheel package. (#350) (Thank you, Jon + Dufresne!) +* Fix annotation-xml typo. (#339) (Thank you, Will Kahn-Greene!) +* Allow uppercase hex chararcters in CSS colour check. (#377) (Thank you, + Komal Dembla, Hugo!) + + +1.0 +~~~ + +Released and unreleased on December 7, 2017. Badly packaged release. + + +0.999999999/1.0b10 +~~~~~~~~~~~~~~~~~~ + +Released on July 15, 2016 + +* Fix attribute order going to the tree builder to be document order + instead of reverse document order(!). + + +0.99999999/1.0b9 +~~~~~~~~~~~~~~~~ + +Released on July 14, 2016 + +* **Added ordereddict as a mandatory dependency on Python 2.6.** + +* Added ``lxml``, ``genshi``, ``datrie``, ``charade``, and ``all`` + extras that will do the right thing based on the specific + interpreter implementation. + +* Now requires the ``mock`` package for the testsuite. + +* Cease supporting DATrie under PyPy. + +* **Remove PullDOM support, as this hasn't ever been properly + tested, doesn't entirely work, and as far as I can tell is + completely unused by anyone.** + +* Move testsuite to ``py.test``. + +* **Fix #124: move to webencodings for decoding the input byte stream; + this makes html5lib compliant with the Encoding Standard, and + introduces a required dependency on webencodings.** + +* **Cease supporting Python 3.2 (in both CPython and PyPy forms).** + +* **Fix comments containing double-dash with lxml 3.5 and above.** + +* **Use scripting disabled by default (as we don't implement + scripting).** + +* **Fix #11, avoiding the XSS bug potentially caused by serializer + allowing attribute values to be escaped out of in old browser versions, + changing the quote_attr_values option on serializer to take one of + three values, "always" (the old True value), "legacy" (the new option, + and the new default), and "spec" (the old False value, and the old + default).** + +* **Fix #72 by rewriting the sanitizer to apply only to treewalkers + (instead of the tokenizer); as such, this will require amending all + callers of it to use it via the treewalker API.** + +* **Drop support of charade, now that chardet is supported once more.** + +* **Replace the charset keyword argument on parse and related methods + with a set of keyword arguments: override_encoding, transport_encoding, + same_origin_parent_encoding, likely_encoding, and default_encoding.** + +* **Move filters._base, treebuilder._base, and treewalkers._base to .base + to clarify their status as public.** + +* **Get rid of the sanitizer package. Merge sanitizer.sanitize into the + sanitizer.htmlsanitizer module and move that to sanitizer. This means + anyone who used sanitizer.sanitize or sanitizer.HTMLSanitizer needs no + code changes.** + +* **Rename treewalkers.lxmletree to .etree_lxml and + treewalkers.genshistream to .genshi to have a consistent API.** + +* Move a whole load of stuff (inputstream, ihatexml, trie, tokenizer, + utils) to be underscore prefixed to clarify their status as private. + + +0.9999999/1.0b8 +~~~~~~~~~~~~~~~ + +Released on September 10, 2015 + +* Fix #195: fix the sanitizer to drop broken URLs (it threw an + exception between 0.9999 and 0.999999). + + +0.999999/1.0b7 +~~~~~~~~~~~~~~ + +Released on July 7, 2015 + +* Fix #189: fix the sanitizer to allow relative URLs again (as it did + prior to 0.9999/1.0b5). + + +0.99999/1.0b6 +~~~~~~~~~~~~~ + +Released on April 30, 2015 + +* Fix #188: fix the sanitizer to not throw an exception when sanitizing + bogus data URLs. + + +0.9999/1.0b5 +~~~~~~~~~~~~ + +Released on April 29, 2015 + +* Fix #153: Sanitizer fails to treat some attributes as URLs. Despite how + this sounds, this has no known security implications. No known version + of IE (5.5 to current), Firefox (3 to current), Safari (6 to current), + Chrome (1 to current), or Opera (12 to current) will run any script + provided in these attributes. + +* Pass error message to the ParseError exception in strict parsing mode. + +* Allow data URIs in the sanitizer, with a whitelist of content-types. + +* Add support for Python implementations that don't support lone + surrogates (read: Jython). Fixes #2. + +* Remove localization of error messages. This functionality was totally + unused (and untested that everything was localizable), so we may as + well follow numerous browsers in not supporting translating technical + strings. + +* Expose treewalkers.pprint as a public API. + +* Add a documentEncoding property to HTML5Parser, fix #121. + + +0.999 +~~~~~ + +Released on December 23, 2013 + +* Fix #127: add work-around for CPython issue #20007: .read(0) on + http.client.HTTPResponse drops the rest of the content. + +* Fix #115: lxml treewalker can now deal with fragments containing, at + their root level, text nodes with non-ASCII characters on Python 2. + + +0.99 +~~~~ + +Released on September 10, 2013 + +* No library changes from 1.0b3; released as 0.99 as pip has changed + behaviour from 1.4 to avoid installing pre-release versions per + PEP 440. + + +1.0b3 +~~~~~ + +Released on July 24, 2013 + +* Removed ``RecursiveTreeWalker`` from ``treewalkers._base``. Any + implementation using it should be moved to + ``NonRecursiveTreeWalker``, as everything bundled with html5lib has + for years. + +* Fix #67 so that ``BufferedStream`` to correctly returns a bytes + object, thereby fixing any case where html5lib is passed a + non-seekable RawIOBase-like object. + + +1.0b2 +~~~~~ + +Released on June 27, 2013 + +* Removed reordering of attributes within the serializer. There is now + an ``alphabetical_attributes`` option which preserves the previous + behaviour through a new filter. This allows attribute order to be + preserved through html5lib if the tree builder preserves order. + +* Removed ``dom2sax`` from DOM treebuilders. It has been replaced by + ``treeadapters.sax.to_sax`` which is generic and supports any + treewalker; it also resolves all known bugs with ``dom2sax``. + +* Fix treewalker assertions on hitting bytes strings on + Python 2. Previous to 1.0b1, treewalkers coped with mixed + bytes/unicode data on Python 2; this reintroduces this prior + behaviour on Python 2. Behaviour is unchanged on Python 3. + + +1.0b1 +~~~~~ + +Released on May 17, 2013 + +* Implementation updated to implement the `HTML specification + `_ as of 5th May + 2013 (`SVN `_ revision r7867). + +* Python 3.2+ supported in a single codebase using the ``six`` library. + +* Removed support for Python 2.5 and older. + +* Removed the deprecated Beautiful Soup 3 treebuilder. + ``beautifulsoup4`` can use ``html5lib`` as a parser instead. Note that + since it doesn't support namespaces, foreign content like SVG and + MathML is parsed incorrectly. + +* Removed ``simpletree`` from the package. The default tree builder is + now ``etree`` (using the ``xml.etree.cElementTree`` implementation if + available, and ``xml.etree.ElementTree`` otherwise). + +* Removed the ``XHTMLSerializer`` as it never actually guaranteed its + output was well-formed XML, and hence provided little of use. + +* Removed default DOM treebuilder, so ``html5lib.treebuilders.dom`` is no + longer supported. ``html5lib.treebuilders.getTreeBuilder("dom")`` will + return the default DOM treebuilder, which uses ``xml.dom.minidom``. + +* Optional heuristic character encoding detection now based on + ``charade`` for Python 2.6 - 3.3 compatibility. + +* Optional ``Genshi`` treewalker support fixed. + +* Many bugfixes, including: + + * #33: null in attribute value breaks XML AttValue; + + * #4: nested, indirect descendant,

    B*efcT=V9Xo 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%0Am4O$u(`4BswlD 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℞7ze1|+dzVr5S%_P90)6p|WgEHw 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